通信和线程池进程池
01 . 线程间通信
-
同一个进程中的多个线程可以直接通信(一个线程可以直接使用另一个线程中产生的数据)
-
通信原则:使用全局变量
from threading import Thread,current_thread '====================方案1=====================' def sum1(x,y): # z是第一个子线程定义的全局变量 global z z = x + y print(current_thread(),a) def func2(): print(current_thread(),z,a) if __name__ == '__main__': # a是在主线程中定义的全局变量 a = 100 t1 = Thread(target=sum1,args=(10,20)) t1.start() t1.join() t2 = Thread(target=func2) t2.start() print('主线程:',current_thread(),z) '====================方案2=====================' def dowload(name): all_data.append(f'{name}数据') if __name__ == '__main__': all_data = [] # 创建子线程同时下载多个电影数据 names = ['电影1','电影2','电影3','电影4'] ts = [] for name in names: t = Thread(target=dowload,args=(name,)) t.start() ts.append(t) # 等到所有电影下载结束,在主线程中处理下载得到的数据 for t in ts: t.join() print(all_data) '===================方案3=====================' def dowload(name): # print(f'{name}数据') time.sleep(randint(3,7)) # 1)添加数据 q.put(f'{name}数据') if __name__ == '__main__': # 使用队列 # 2) 创建队列对象 q = Queue() # 创建子线程同时下载多个电影数据 names = ['电影1', '电影2', '电影3', '电影4'] for name in names: t = Thread(target=dowload, args=(name,)) t.start() # 3) 获取队列中的数据 # 队列的get操作有等待功能:如果在执行get的时候队列为空,代码不会报错,而是停留当前位置,直到队列不为空或者超时为止。 for _ in range(4): print(q.get(timeout=8))
02 . 进程间通信
-
不同进程中的数据无法直接共享,如果进程间想要通信(数据传递)必须使用进程队列
- from multiprocessing import Process,Queue(进程队列,支持多进程通信)
- from queue import Queue(这个是线程队列,不能进行进程间通信)
-
进程队列的使用方法
-
创建全局进程队列对像
-
将队列对象作为参数传递到进程中,如果使用队列的进程和创建队列的进程不一致就必须要通过参数传递到队列,一致直接使用。
-
在任意进程中使用队列获取数据
from threading import Thread,current_thread from queue import Queue import time from random import randint from multiprocessing import Process,Queue,current_process def func1(queue): print('func1:',current_process()) queue.put(100) def func2(queue): print('func2:',current_process()) queue.put(200) def func3(): print('func3',current_process()) q.put(300) if __name__ == '__main__': x = 100 all_data = [] q = Queue() print('创建队列:',current_process()) p1 = Process(target=func1,args=(q,)) p2 = Process(target=func2, args=(q,)) p1.start() p2.start() t1 = Thread(target=func3()) t1.start() print(q.get(timeout=2)) print(q.get(timeout=2)) print(q.get(timeout=2))
-
03 . 队列如何正确结束
-
创建队列,并且创建一个子线程获取队列中的数据
-
创建多个线程同时获取多个数据
-
等到所有的任务都完成,把队列结束标志添加到队列中
from queue import Queue import time from random import randint from threading import Thread def dowloda(name): time.sleep(randint(1,10)) q.put(f'{name}数据') def get_data(): while True: data = q.get() if data == 'end': break print(data) if __name__ == '__main__': q = Queue() t2 = Thread(target=get_data) t2.start() ts = [] for x in range(randint(30,50)): t = Thread(target=dowloda,args=(f'电影{x}',)) t.start() ts.append(t) for t in ts: t.join() q.put('end')
04 . 线程池
-
线程池:一个线程池中有多个线程,并且可以添加多个任务(任务的数量可以比线程的数量多),线程池会自动给线程池的线程分配任务,知道所有的任务都完成。
-
使用线程池
- 创建线程池:ThreadPoolExecutor(线程数)
- 添加任务
- 任务一个一个的添加到线程池中:线程池对象 . submit(任务对应的函数,实参1,实参2,…)
- 同时添加多个任务 - 任务对应的函数有且只有一个参数 : 线程池对象 . map(函数,实参对应的序列)
-
关闭线程池并且等待线程池的任务结束
-
关闭线程池指的是停止向线程池中添加任务
-
线程池关闭之前可以随时添加任务
-
不能在线程池关闭以后添加任务
from queue import Queue import time from random import randint from threading import Thread,current_thread # 导入线程池对应的类 - 线程池执行者 from concurrent.futures import ThreadPoolExecutor def dowloda(name): time.sleep(randint(2,4)) print(f'{name}数据') if __name__ == '__main__': names = [f'电影{x}'for x in range(50)] pool = ThreadPoolExecutor(5) # for x in names: # pool.submit(dowloda,x) pool.map(dowloda,names) pool.shutdown()
-
05 . 进程池
-
创建进程池对象
-
添加任务
-
一次添加一个任务
# pool.apply_async(download, args=('肖申克的救赎',)) # pool.apply_async(func1, args=(100, 200)) # for x in range(10): # pool.apply_async(download, args=(f'电影{x}',))
-
同时添加多个任务
pool.map_async(download, ['电影1', '电影2', '电影3'])
-
-
关闭进程池(通过async方式添加的任务,必须在任务添加结束后依次执行close和join操作,任务才会启动)
from multiprocessing import Pool, current_process import time from random import randint def download(name): time.sleep(randint(2, 6)) print(f'{name}数据', current_process()) def func1(x, y): print(x, y) if __name__ == '__main__': # 1. 创建进程池对象 pool = Pool(5) # 2. 添加任务 # 1) 一次添加一个任务 # pool.apply_async(download, args=('肖申克的救赎',)) # pool.apply_async(func1, args=(100, 200)) # for x in range(10): # pool.apply_async(download, args=(f'电影{x}',)) # 2) 同时添加多个任务 pool.map_async(download, ['电影1', '电影2', '电影3']) # 3. 关闭进程池(通过async方式添加的任务,必须在任务添加结束后依次执行close和join操作,任务才会启动) pool.close() pool.join()