1. JoinableQueue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import JoinableQueue # 可以被join的队列,join是等待摸个任务完成,able 可以怎么着, Queue是对垒 q = JoinableQueue() q.put("1") q.put("2") print("取走了一个%s " % q.get()) q.task_done() # task_down 告诉队列这个数据已经处理完了 # 该函数不是表示任务全部处理完成,而是取出某个数据处理完成 print("再取走了%s" % q.get()) q.task_done() print("......") # q.join() #等待队列中的数据处理完毕 , join, task_down的次数 == put的次数 print("Game Over")
生产者消费者问题:
生产者生产结束以后,消费者一直循环吃,吃完以后,消费者并不知道
生产者已经生产结束,就会一直等待生产者生产东西
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time import random from multiprocessing import JoinableQueue, Process 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)) def eat_hotdog(name, q): while True: hotdog = q.get() time.sleep(random.randint(1, 3)) print("%s吃掉了%s" % (name, hotdog)) # 每次处理完成一个数据,必须记录该数据 q.task_done() if __name__ == '__main__': q = JoinableQueue() p1 = Process(target=make_hotdog, args=("jerry", q)) p2 = Process(target=make_hotdog, args=("monkey", q)) p3 = Process(target=make_hotdog, args=("owen", q)) c1 = Process(target=eat_hotdog, args=("思聪", q)) c1.daemon = True c2 = Process(target=eat_hotdog, args=("健林", q)) c2.daemon = True p1.start() p2.start() p3.start() c1.start() c2.start() # 明确商家生产完毕,在明确消费者吃完了,就算结束 p1.join() print("第一家生产完毕") p2.join() print("第二家生产完毕") p3.join() print("第三家生产完毕") # 消费者吃完了 q.join() print("消费者吃完了")
2. 多线程
2.1 什么是线程:
线程指的是一条流水线的工作过程的总称
线程是CPU的基本执行单位
对比进程而言,进程仅仅是一个资源单位其包含了程序运行所需的资源,就像一个车间
而单有资源是无法生产出产品的,必须有具体的生产产品的逻辑代码
线程就相当于车间中的一条流水线,而你的代码就是流水线上的一道道工序
2.2 特点:
1.每个进程都会有一个默认的线程
2.每个进程可以存在多个线程
3.同一进程中的所有线程之间数据是共享的
4.创建线程的开销远比创建进程小的多
2.3 主线程与子线程的区别:
1.线程之间是没有父子之分,是平等的
2.主线程是由操作系统自动开启的,而子线是由程序主动开启
3.即时主线程的代码执行完毕,也不会结束进程,会等待所有线程执行完毕,进程才结束
2.4 开启线程的两种方式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 方式一 直接实例化Thread类 from threading import Thread def task(): print("子线程 is running") t = Thread(target=task) t.start() # 执行的顺序不固定,一般来说,开启子线程的速度远远高于继续执行主线程的速度 print("主线程 is runnning") # # # 方式二:自定义类继承thread类 class MyThread(Thread): def run(self): print("子线程 is running") t = MyThread() t.start() print("主线程 is running")
2.5 同一个进程之间数据共享
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time,os from threading import Thread x = 100 def task(): global x x = 10 print("线程",os.getpid()) # 与主线程的pid相同 t = Thread(target=task) t.start() # 主线程等待子线程执行完毕 time.sleep(3) print(x) # 10 print("主线程",os.getpid())
2.6 守护线程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 守护线程 # 主线程代码结束后,不会立即结束,会等待其他所有非守护线程结束 # 守护进程会等到所有非守护线程结束后立即结束,前提是除了主线程外,还有别的非守护进程 # 当然如果守护线程已经执行完成,立马就结束了,就不会再等待其他的非守护线程 # 与守护进程的差别 # 守护进程会等到主进程结束后立即结束,虽然主进程会等待其他非守护进程的 # 子进程结束,但是守护进程不会继续等待,守护进程已经提前挂了 import time from threading import Thread def task1(): print("子进程1 is running") time.sleep(3) print("子进程1 is over") def task2(): print("子进程2 is running") time.sleep(2) print("子进程2 is over") t1 = Thread(target=task1) t2 = Thread(target=task2) t1.daemon = True t1.start() t2.start() print("主进程 Game Over")
2.7 互斥锁与死锁
一旦共享必然出现竞争问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Lock, enumerate,Thread import time num = 10 lock = Lock() def task(): global num lock.acquire() a = num time.sleep(0.1) num = a - 1 lock.release() for i in range(10): t = Thread(target=task) t.start() for t in enumerate()[1:]: t.join() print(num)
死锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" 死锁问题 当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源要想使用必须同时具备两把锁 这时候程序就会进程无限卡死状态 ,这就称之为死锁 例如: 要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子 如何避免死锁问题 锁不要有多个,一个足够 如果真的发生了死锁问题,必须迫使一方先交出锁 """ # 死锁案例 from threading import Thread,Lock,current_thread # 筷子 import time lock1 = Lock() # 盘子 lock2 = Lock() def task1(): lock1.acquire() print("%s抢占了筷子" % current_thread().name) time.sleep(0.5) lock2.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) def task2(): 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=task1) t2 = Thread(target=task2) t1.start() t2.start()
2.8 RLock 与 信号量
RLock
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" Rlock 了解 称之为递归锁 或者可重入锁 Rlock不使用用来解决死锁问题的 与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire """ from threading import Thread,RLock rlock = RLock() def task(): rlock.acquire() print("子线程测试") rlock.acquire() rlock.acquire() print("Game Over") rlock.release() print("123") t = Thread(target=task) t.start() # Game Over # 123
信号量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" 信号量 了解 Lock RLock 可以现在被锁定的代码 同时可以被多少线程并发访问 Lock 锁住一个马桶 同时只能有一个 Semaphore 锁住一个公共厕所 同时可以来一堆人 用途: 仅用于控制并发访问 并不能防止并发修改造成的问题 """ from threading import Semaphore, Thread import time s = Semaphore(2) # 一次可以多少个去厕所 def task(): s.acquire() print("Game Start") time.sleep(2) print("Game Over") s.release() for i in range(10): t = Thread(target=task) t.start()