joinablequeue:
# join是等待某个任务完成 able 可以怎么着 Queue 是队列
# 可以被join的队列
q = JoinableQueue()
print("_______________")
q.put("123")
q.put("123")
# print("取走了一个%s" % q.get())
# task_done告诉队列这个数据已经被处理完了
q.task_done() #注意:该函数 不是表示任务全部处理完成 而是 取出来某个数据处理完成
# print("再取走了一个%s" % q.get())
# q.task_done()
print("..............")
q.join() # 等待队列中的数据被处理完毕 join task_done的次数 == put的调用次数
print("over")
import random import time from multiprocessing import Process, Queue,JoinableQueue def make_hotdog(name,q): for i in range(5): time.sleep(random.randint(1,3)) print("%s生产了热狗%s" % (name,i)) q.put("%s的%s号热狗" % (name,i)) # q.put(None) def eat_hotdog(name,q): while True: hotdog = q.get() # if not hotdog: # break time.sleep(random.randint(1, 3)) print("%s吃掉了%s" % (name,hotdog)) # 必须记录 该数据 处理完成了 q.task_done() if __name__ == '__main__': q = JoinableQueue() p = Process(target=make_hotdog,args=("owen的热狗店",q)) p2 = Process(target=make_hotdog,args=("bgon的热狗店",q)) p3 = Process(target=make_hotdog,args=("jerry的热狗店",q)) c = Process(target=eat_hotdog,args=("思聪",q)) c.daemon = True c2 = Process(target=eat_hotdog, args=("大葱", q)) c2.daemon = True # c3 = Process(target=eat_hotdog, args=("二葱", q)) p.start() p2.start() p3.start() c.start() c2.start() # c3.start() # 目前的思路 是当商家做完以后 放一个None 作为结束标志 不够好 必须明确商家和消费者的个数 # 明确商家生成完毕 在明确消费者吃完了 就算结束 p.join() print("第一家生成完毕") p2.join() print("第二家生成完毕") p3.join() print("第三家生成完毕") # 消费者吃完了 q.join() print("消费吃完了!") print("美好的一天结束了!")
什么是线程:
线程是操作系统最小的运算调度单位,被包含在进程中,一个线程一个固定的执行流程。
线程与进程的关系 重点:
线程不能单独存在,必须存在于进程中,进程是一个资源单位,而线程才是执行单位,进程包含了运行程序所需的所有资源。
没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程。
当我们启动一个程序时,操作系统就会自己为这个程序创建一个主线程。
线程可以由程序后期开启,自己开启线程称之为子线程。
为什么需要线程:
目的只有一个就是为了提高效率,就像一个车间,如果产量跟不上,就在造一条流水线。
当然也可以重新造一个车间然后把材料运过去,然而这个过程是耗时的。
所以通常情况是创建新的流水线而不是车间(进程)即线程。
如何使用:
使用方法和多进程一模一样。
不过开启线程的代码可以放在任意位置,开启进程必须放在判断下面
from threading import Thread,current_thread import time def task(): print("2",current_thread()) print("子线程running") time.sleep(10) print("子线程over") # 使用方法一 直接实例化Thread类 if __name__ == '__main__': t = Thread(target=task) t.start() # task() # 执行顺序不固定 如果开启线程速度足够快 可能子线程先执行 print("主线程over") print("1",current_thread()) # 使用方法二 继承Thread 覆盖run方法 class MyThread(Thread): def run(self): print("子线程run!") m = MyThread() print("主线over")
from threading import Thread,current_thread import time def task(): print("2",current_thread()) print("子线程running") time.sleep(10) print("子线程over") # 使用方法一 直接实例化Thread类 if __name__ == '__main__': t = Thread(target=task) t.start() # task() # 执行顺序不固定 如果开启线程速度足够快 可能子线程先执行 print("主线程over") print("1",current_thread()) # 使用方法二 继承Thread 覆盖run方法 class MyThread(Thread): def run(self): print("子线程run!") m = MyThread() print("主线over")
使用方法和多进程一模一样,开启线程的代码可以放在任意位置,放在外面和判断里面是没有
什么区别的,但是进程的代码必须放在判断里面。
线程的特点:
1.线程的创建开销比进程小
2.一个进程内的多个线程可以共享同一个进程内的资源。
3.线程内没有父子线程之分,线程之间是平等的。所有线程的PID相同。
案例:
import os from threading import Thread from multiprocessing import Process import time def task(): # print("hello") print(os.getpid()) pass if __name__ == '__main__': st_time = time.time() ts = [] for i in range(100): t = Thread(target=task) # t = Process(target=task) t.start() ts.append(t) for t in ts: t.join() print(time.time()-st_time) print("主over")
守护线程:
一个线程可以设置为另一个线程的守护线程
特点:被守护的线程结束后守护线程也会结束。
守护线程在主线程结束还会等非守护线程结束才结束,当然前提是有其他的非守护线程,另外守护线程也可以提前结束,所以说前提都是得守护线程还在运行。守护进程则是一旦主进程运行结束那么守护进程不会等非守护进程结束,会提前结束。
所以如果守护线程已经完成任务,那天也立马就结束了。
from threading import Thread import time def task(): print("子1running......") time.sleep(100) print("子1over......") def task2(): print("子2running......") time.sleep(4) print("子2over......") t = Thread(target=task) t.daemon = True t.start() t2 =Thread(target=task2) t2.start() print("主over")
线程互斥锁:
共享意味着竞争,线程中也存在安全问题,多线程可以并发执行,一旦并发了并且访问了同一个资源
就会有问题。解决方案还是互斥锁
案例:
from threading import Thread,enumerate,Lock import time number = 10 lock = Lock() def task(): global number lock.acquire() a = number time.sleep(0.1) number = a - 1 lock.release() for i in range(10): t = Thread(target=task) t.start() for t in enumerate()[1:]: # print(t) t.join() print(number)
死锁问题:
死锁问题:
当程序出现了不止一把锁,分别被不同的线程持有,有一个资源要想使用必须同时具备两把锁,这个时候程序就会无限卡死状态,这几称之为死锁。
例如:
要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子
如何避免死锁问题
锁不要有多个,一个足够
如果真的发生了死锁问题,必须迫使一方先交出锁
import time # 盘子 lock1 = Lock() # 筷子 lock2 = Lock() def eat1(): lock1.acquire() print("%s抢到了盘子" % current_thread().name) time.sleep(0.5) lock2.acquire() print("%s抢到了筷子" % current_thread().name) print("%s开吃了!" % current_thread().name) lock2.release() print("%s放下筷子" % current_thread().name) lock1.release() print("%s放下盘子" % current_thread().name) def eat2(): lock2.acquire() print("%s抢到了筷子" % current_thread().name) lock1.acquire() print("%s抢到了盘子" % current_thread().name) print("%s开吃了!" % current_thread().name) lock1.release() print("%s放下盘子" % current_thread().name) lock2.release() print("%s放下筷子" % current_thread().name) t1 = Thread(target=eat1) t2 = Thread(target=eat2) t1.start() t2.start()
可重入锁:
Rlock 称之为递归锁或者可重入锁
Rlock不是用来解决死锁问题的
与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire
from threading import RLock, Lock, Thread # l = Lock() # # l.acquire() # print("1") # l.acquire() # print("2") l = RLock() # l.acquire() # print("1") # l.acquire() # print("2") def task(): l.acquire() print("子run......") l.release() # 主线程锁了一次 l.acquire() l.acquire() l.release() l.release() t1 = Thread(target=task) t1.start()
信号量:
信号量 了解
Lock RLock
可以现在被锁定的代码 同时可以被多少线程并发访问
Lock 锁住一个马桶 同时只能有一个
Semaphore 锁住一个公共厕所 同时可以来一堆人
用途: 仅用于控制并发访问 并不能防止并发修改造成的问题
from threading import Semaphore, Thread import time s = Semaphore(5) def task(): s.acquire() print("子run") time.sleep(3) print("子over") s.release() for i in range(10): t = Thread(target=task) t.start()