JoinableQueue的使用
from multiprocessing import JoinableQueue, Queue q = JoinableQueue() q.put(1) q.put(2) print(q.get()) q.task_done() # 告诉容器已经处理完了一个数据 q.task_done() # 有几次就要调用几次 q.join() # 也是一个阻塞函数 一直到队列中的数据被处理完毕(task_done的调用次数等于队列中数据数量) print('处理完毕') print(q.get()) print(q.empty())
生产者消费者模型
''' 小明作为消费者 要吃热狗 生产者 负责做热狗 问题: 小明不清楚对方会生产多少热狗 ''' from multiprocessing import Process, Queue, JoinableQueue import time, random # 生产者 def make_hot_dog(q): for i in range(1, 6): time.sleep(random.randint(1, 3)) print('生产者 生产了hot_dog%s' % i) q.put('hot_dog%s' % i) # 消费者 def eat_hot_dog(q): while True: time.sleep(random.randint(1, 2)) hot_dog = q.get() print('小明吃了%s' % hot_dog) q.task_done() if __name__ == '__main__': # 共享数据的队列 q = JoinableQueue() # 生产者 p1 = Process(target=make_hot_dog, args=(q,)) p2 = Process(target=make_hot_dog, args=(q,)) p1.start() p2.start() # 消费者 c1 = Process(target=eat_hot_dog, args=(q,)) c1.daemon = True c1.start() # 先要确定生产者已经不会再生产了 p1.join() p2.join() print('生产已经结束了???') # 再确定队列中的数据都被处理完成 q.join() print('小明已经全部吃完了。。。') c1.terminate() # 小明就不需要在吃了
线程
线程指的是一条流水线,整个执行过程中的总称,也是一个抽象概念
线程是CPU的最小执行单位,是具体负责执行代码的
进程是一个资源但闻,其中包括了该程序运行所需的所有子资源
线程的特点:
一个进程中至少包含一个线程,是由操作系统自动创建的,称之为主线程
一个进程中可以有任意数量的线程
创建线程的开销对比而言 要小的多
同一个进程中的线程间数据是共享的(最主要的特点)
如何使用:使用的方式与进程一致
不同的是:创建线程的代码 可以写在任何位置
from threading import Thread 第一种 开启线程的方式 直接实例化Thread类 def task(): print('running .....') t = Thread(target=task) t.start()
2.继承Thread类 覆盖run方法 class My(Thread): def run(self): print('running....') t = My() t.start()
开启线程速度比开启进程快很多
主线程任务执行完毕后 进程不会立即结束 会等待所有子线程全部执行完毕
在同一个进程 所有线程都是平等的 没有子父这么一说
from threading import Thread import time def task(): print('子线程 running 。。。') time.sleep(3) print('子线程over。。。') t = Thread(target=task) t.start() print('main over')
线程与进程的区别
一:数据是共享的
二:创建进程 与创建线程的开销 线程比进程快大约是一百多倍
线程安全也是通过锁来保证,锁的用法与进程中的锁一模一样
import time from threading import Thread, Lock num = 10 lock = Lock() def task(): global num lock.acquire() a = num time.sleep(0.1) num = a - 1 lock.release() ts = [] for i in range(10): t = Thread(target=task) t.start() ts.append(t) for t in ts: t.join() print(num)
死锁:
当你今后在开发一些高并发程序时 很有可能出现线程/进程安全问题
解决方案只能加锁但是在使用锁时,很有可能出现死锁问题
同一把锁调用了多次acquire 导致死锁问题(最low的死锁问题)
有多把锁一个线程抢到一把锁,要完成任务必须同时抢到所有锁,这将导致死锁问题
如何避免:
1.能不加锁就不加锁
2.如果一定要加 要保证锁只有一把
线程对象的常用属性
from threading import Thread # Thread对象常用属性 t = Thread(name='一个线程') print(t.name) print(t.is_alive()) # 线程名称 print(t.daemon) print(t.ident) # threading 模块中的常用属性 import threading import os t = threading.current_thread() # 获取当前线程对象 print(t) print(os.getpid()) def task(): print(threading.current_thread()) print(threading.active_count()) # 获取目前活跃的线程数量 Thread(target=task).start() print(threading.active_count()) # 正在运行中的线程数量 ts = threading.enumerate() # 返回所有正在运行的线程对象 print(ts)
信号量 可以控制同一时间 有多少线程可以并发的访问
不是用来处理线程安全问题
守护线程
守护线程会在主线程结束后立即结束 即使任务没有完成
主线程 会等待所有子线程全部完成后结束
守护线程会在所有非守护线程结束后 结束
主线 守护进程
主线程要等待所有子线结束
from threading import Thread, current_thread import time def task1(): print('%s正在运行。。。' % current_thread().name) time.sleep(3) print('%s over 。。。' % current_thread().name) def task2(): print('%s正在运行。。。' % current_thread().name) time.sleep(10) print('%s over 。。。' % current_thread().name) t1 = Thread(target=task1) t2 = Thread(target=task2) t1.start() t2.start() print('over')