线程池与进程池
池是一个容器的概念,所谓进程池,线程池,就是建立好的进程或线程,在一个容器对象中,且可以重复使用。开启进程与线程都需要消耗资源,两者相比,线程消耗资源更少一些。但是为了能在计算机硬件承受范围内最大限度地利用计算机资源,可以使用线程池与进程池。同时进程池线程池限制了程序云效率,但是保证了计算机安全。
创建线程池与线程池都在concurrent.future模块下ThreadPoolExeutor(线程池),ProcessPoolExecutor(进程池)。首先需要进线程池创建对象,开启进线程时需要用到submit()方法,且都有返回结果,结果对象需要用到result()获取结果。进线程对象都有shutdown()方法关闭对象。
''' 线程池 两种方式 ''' import time from concurrent.futures import ThreadPoolExecutor # 调用线程池 pool = ThreadPoolExecutor(5) # 创建线程池对象 线程创建个数默认是当前计算cup个数乘以5,现在设定5个 def func(n): print(f'{n}来了') time.sleep(2) return f'{n}走了' t_list = [] for i in range(20): # 开启20个线程 res = pool.submit(func, f'{i}号伞兵') # 开启线程, 获取结果对象 t_list.append(res) # 添加到列表中 for k in t_list: print(k.result()) # 打印结果 ''' 线程池处理的第二种方式 ''' import time from concurrent.futures import ThreadPoolExecutor # 调用线程池 pool = ThreadPoolExecutor(5) # 创建线程池对象 def func(n): print(f'{n}来了') time.sleep(2) return f'{n}走了' def call_back(m): # 返回结果处理函数 print(m.result()) for i in range(20): pool.submit(func, f'{i}号伞兵').add_done_callback(call_back) # 使用add_done_callback设置返回结果处理 ''' 进程池两种方式 ''' import time from concurrent.futures import ProcessPoolExecutor # 调用进程池 pool = ProcessPoolExecutor(5) # 创建进程池 默认是cup个数 def func(n): print(f'{n}来了') time.sleep(2) return f'{n}走了' if __name__ == '__main__': t = [] for i in range(20): res = pool.submit(func, f'{i}号伞兵') t.append(res) for k in t: print(k.result()) pool.shutdown() ''' 创建进程第二种方式 ''' import time from concurrent.futures import ProcessPoolExecutor # 调用进程池 pool = ProcessPoolExecutor(5) # 创建进程池 默认是cup个数 def func(n): print(f'{n}来了') time.sleep(2) return f'{n}走了' def call_back(m): print(m.result()) if __name__ == '__main__': for i in range(20): pool.submit(func, f'{i}号伞兵').add_done_callback(call_back)
协程
协程是程序员自定义一名词,是对单线程下实现并发,称之为协程。并发是一种现象:是主观上看起来计算机同时执行多个任务。实现并发计算的多道技术就是并发的一种体现,当进程遇到I/O操作时计算机便会保存当前状态,切换到下一个事件的计算。因此要做到切换和保存状态边可以实现并发。在python中使用genvent包中spawn和mokey模块,可以做到保存状态加切换。
import time from gevent import spawn, monkey;monkey.patch_all() # monkey.patch_all() 检测I/O def func1(): print(111) time.sleep(1) # 模拟I/O阻塞 print(111) def func2(): print(222) time.sleep(2) print(222) spawn(func1) # spawn模块会检测函数中的I/O,检测到I/O状态,就会调到其他非阻塞 res = spawn(func2) # 检测func2 res.join() # 等待
利用协程实现单核下TCP并发通信
''' 服务端 ''' import socket from gevent import spawn, monkey monkey.patch_all() server = socket.socket() server.bind(('127.0.0.1', 8081)) server.listen(5) def talk(conn): while True: try: data = conn.recv(1024) if len(data) == 0: break print(data.decode('utf-8')) conn.send(data.upper()) except BaseException as e: print(e) break conn.close() def server1(): while True: conn, addr = server.accept() spawn(talk, conn) # 检测talk函数,传入conn参数 if __name__ == '__main__': res = spawn(server1) # 检测阻塞态 res.join() ''' 服务端 ''' import socket from threading import Thread,current_thread def clint1(): clint = socket.socket() clint.connect(('127.0.0.1', 8081)) i = 0 while True: msg = '%s--%s' % (current_thread().name, i) clint.send(msg.encode('utf-8')) data = clint.recv(1024) print(data.decode('utf-8')) i += 1 for i in range(400): # 开启400个线程,访问服务端 t = Thread(target=clint1) print(current_thread().name) t.start()
写成应用应根据任务不同来使用,对于计算密集型的任务来说,不适合用协程,但对于I/O密集型协程是适合的。
对于并发:可以开多进程,多进程下开多线程,多线程下还可以开多协程!!!