并发编程之进程池,线程池 和 异步回调,协程
1.进程池和线程池
2.异步回调
3.协程
4.基于TCP使用多线程实现高并发
一.进程池和线程池
什么是进程池和线程池:
''' 池 Pool 指的是一个容器 线程池就是用来存储线程对象的 容器创建池子 可以指定池子里面有多少线程,如果不指定就默认为CUP个数*5 不会立即开启线程,会等到有任务提交后在开启线程线程池,不仅帮我们管理了线程的开启和销毁,还帮我们管理任务的分配特点: 线程池中的线程只要开启之后 即使任务结束也不会立即结束 因为后续可能会有新任务避免了频繁开启和销毁线程造成的资源浪费1.创建一个线程池2.使用submit提交任务到池子中 ,线程池会自己为任务分配线程进程池与线程池:用法都是一样的 那么为什么要使用池子:因为使用池子可以限制并发的任务数目,在计算机可以承受的范围内去并发执行任务那么既然提到了提交任务,那提交任务分为哪几种同步:提交任务之后 原地等待任务的返回结果,期间不做任何事异步:提交任务之后 不等待任务的返回结果(异步的结果怎么拿:通过异步回调拿) 直接执行下一行代码注意:池子中创建的进程和线程创建一个就不会创建了,至始至终使用的都是最初的那几个,这样话会节省开辟进程和线程的资源 '''
案例:使用代码实现线程池
1 # 线程池 2 from concurrent.futures import ThreadPoolExecutor 3 import time 4 import os 5 6 pool = ThreadPoolExecutor(5)# 括号内可以传参数指定线程池内的线程个数 7 # 也可以不传,不传默认是当前所在计算机的cpu个数*5 8 9 def task(n): 10 print(n,os.getpid())# 查看当前线程号 为了验证线程创建就不会变了 11 time.sleep(2) 12 13 for i in range(20): 14 pool.submit(task,i)# 朝池子中提交任务 属于异步提交 15 print('主')
View Code
案例:使用代码实现进程池
1 # 进程池 2 from concurrent.futures import ProcessPoolExecutor 3 import time 4 import os 5 6 pool = ProcessPoolExecutor()#不传默认是计算机cpu的个数 7 8 def task(n): 9 print(n,os.getpid())# 查看当前进程号 10 time.sleep(2) 11 12 if __name__ == '__main__': 13 14 for i in range(20): 15 pool.submit(task,i)# 朝池子中提交任务 属于异步提交 16 print('主')
View Code
二.异步回调
那么如何通过异步回调拿到结果:
如何通过异步回调拿到结果异步回调异步指的是任务的提交方式是异步的异步任务的问题: 如果这个任务执行完成后会产生返回值,任务发起方该何时去获取结果解决方案: 异步回调 异步回调指的就是一个函数,该函数会在任务后自动被调用,并且会传入Future对象 , 通过Future对象的result()获取执行结果 , 有了回调函数 就可以在任务完成时 及时处理它
第一种方式:
1 # 第一种方式 2 # 线程池和进程池 3 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 4 import time 5 import os 6 7 # pool = ThreadPoolExecutor(5)# 括号内可以传参数指定线程池内的线程个数 8 # 也可以不传,不传默认是当前所在计算机的cpu个数*5 9 10 pool = ProcessPoolExecutor()# 不传默认就是计算机cpu个数 11 12 def task(n): 13 print(n,os.getpid())# 查看当前进程号 为了验证线程创建就不会变了 14 time.sleep(2) 15 return n **2# 返回值 16 17 if __name__ == '__main__': 18 19 t_list = []# 先一次性把所有的线程都起来放入这个列表里 20 for i in range(20): 21 22 res = pool.submit(task,i)# 朝池子中提交任务 属于异步提交 23 # print(res.result())# 原地等待任务的返回结果,这又变成了同步 24 t_list.append(res) 25 26 pool.shutdown()# 关闭池子,等待池子中所有的任务执行完毕之后,才会往下运行代码 27 # 这个可以让20 个线程先运行结束在拿结果 28 for p in t_list: 29 print('>>>',p.result())# 通过返回值点result拿结果
View Code
注意:但是第一种解决方案是有问题的
之前说过异步提交这个结果,一旦有结果就会有对应的机制取处理他, 但是上面的第一种方式并没有处理,上面那种方式只是用for 循环然后人为的认为 第一个就是他的结果第二个就是下一个的结果
最终版本异步回调:
1 # 线程池和进程池 2 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 3 import time 4 import os 5 6 # pool = ThreadPoolExecutor(5)# 括号内可以传参数指定线程池内的线程个数 7 # 也可以不传,不传默认是当前所在计算机的cpu个数*5 8 9 pool = ProcessPoolExecutor()# 不传默认就是计算机cpu个数 10 11 def task(n): 12 print(n,os.getpid())# 查看当前进程号 为了验证线程创建就不会变了 13 time.sleep(2) 14 return n **2# 返回值 15 16 def call_back(x): 17 print('拿到了异步提交任务的返回结果:',x.result()) 18 #异步回调机制:当异步提交的任务有返回结果之后,会自动触发回调函数的执行 19 if __name__ == '__main__': 20 21 t_list = []# 先一次性把所有的线程都起来放入这个列表里 22 for i in range(20): 23 24 res = pool.submit(task,i).add_done_callback(call_back)# 提交任务的时候,给每个任务都绑定一个回调函数 25 # 一旦该任务有结果,就会立刻执行对应的回调函数 26 t_list.append(res)
View Code
三.协程
''' 协程进程:资源单位线程:执行单位协程:单线程下实现并发并发是什么:切换+保存状态:看起来像同时执行的,就可以称之为并发协程:完全就是程序员自己定义出来的名词,就是单线程下实现并发那么并发需要哪些条件:多道技术空间上的复用时间上的复用切换+保存状态程序员自己通过代码自己检测程序中的IO 一旦遇到IO自己通过代码切换 给操作系统的感觉是你这个线程没有任何的IO ps:欺骗操作系统 让它误认为你这个程序一直没有IO 从而保证程序在运行态和就绪态来回切换 提升代码的运行效率切换+保存状态就一定能够提升效率吗??? 当你的任务是iO密集型的情况下 提升效率 如果你的任务是计算密集型的 降低效率所以需要找到一个能够识别IO的一个工具 gevent模块 那么因为gevent模块没有办法自动识别time.sleep等io请况,需要自己配置一个参数 需要注意: 1.如果主线程结束了 协程任务也会立即结束。 2.monkey补丁的原理是把原始的阻塞方法替换为修改后的非阻塞方法,即偷梁换柱,来实现IO自动切换 必须在打补丁后再使用相应的功能,避免忘记,建议写在最上方 '''
案例:
1 from gevent import monkey;monkey.patch_all() # 由于该模块经常被使用,所以建议写在一行 2 from gevent import spawn 3 import time 4 5 def heng(): 6 print("哼") 7 time.sleep(2) 8 print('哼') 9 10 def ha(): 11 print('哈') 12 time.sleep(3) 13 print('哈') 14 15 def heiheihei(): 16 print('嘿嘿嘿') 17 time.sleep(5) 18 print('嘿嘿嘿') 19 20 21 start = time.time() 22 g1 = spawn(heng)# spawn会检测所有的任务 23 g2 = spawn(ha) 24 g3 = spawn(heiheihei) 25 26 g1.join() 27 g2.join()# 如果没有join直接就结束了,因为主线程都结束了 28 g3.join() 29 print(time.time() - start)
View Code
TCP单线程实现并发:
服务端:
1 from gevent import monkey;monkey.patch_all() 2 import socket 3 from gevent import spawn 4 5 server = socket.socket() 6 server.bind(('127.0.0.1',8080)) 7 server.listen(5) 8 9 def talk(conn): 10 while True: 11 try: 12 data = conn.recv(1024) 13 if not data:break 14 print(data.decode('utf-8')) 15 conn.send(data.upper()) 16 except ConnectionResetError as e: 17 print(e) 18 break 19 conn.close() 20 21 def server1(): 22 while True: 23 conn,addr = server.accept() 24 spawn(talk,conn)# 监测和调用这个函数 25 26 if __name__ == '__main__': 27 g1 = spawn(server1)# 也是监测和调用 28 g1.join()
View Code
客户端:
1 import socket 2 from threading import Thread,current_thread 3 4 5 def client(): 6 client = socket.socket() 7 client.connect(('127.0.0.1',8080)) 8 n = 0 9 while True: 10 11 data = '%s %s'%(current_thread().name,n) 12 client.send(data.encode('utf-8')) 13 res = client.recv(1024) 14 print(res.decode('utf-8')) 15 n += 1 16 17 18 for i in range(400): 19 t = Thread(target=client) 20 t.start()
View Code
四.基于TCP使用多线程实现高并发
'''服务端 1 要有固定的ip和port 2 24小时不间断提供服务 3 能够支持并发 TCP服务端实现并发1 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 1 将连接循环和通信循环拆分成不同的函数2.将通信循环做成多线程
服务端:
1 import socket 2 from threading import Thread 3 4 server = socket.socket() 5 server.bind(('127.0.0.1',8080)) 6 server.listen(5) 7 8 def talk(conn): 9 while True: 10 try: 11 data = conn.recv(1024) 12 if not data:break 13 print(data.decode('utf-8')) 14 conn.send(data.upper()) 15 except ConnectionResetError as e: 16 print(e) 17 break 18 conn.close() 19 20 21 while True: 22 conn,addr = server.accept()# 监听,等待客户端的连接,阻塞态 23 t = Thread(target=talk,args=(conn,)) 24 t.start()
View Code
客户端:
1 import socket 2 client = socket.socket() 3 client.connect(('127.0.0.1',8080)) 4 while True: 5 msg = input('msg:') 6 if not msg:continue 7 client.send(msg.encode('utf-8')) 8 client.recv(1024) 9 client.close()
View Code
但是以上案例是有一个致命的缺陷的:
但是用这种方法有很大的缺点,就是万一有成千上万的客户端来连, 就需要开启对应的线程,就算线程开销再小,也是有消耗的, 这样会导致内存被占满,计算机崩溃,需要用线程池才可以解决问题
解决方案就是使用线程池:
服务端:
1 import socket 2 from concurrent.futures import ThreadPoolExecutor 3 tpool = ThreadPoolExecutor(3) 4 5 def communicate(conn): 6 while True: 7 try: 8 data = conn.recv(1024) 9 if not data: break 10 conn.send(data.upper()) 11 except ConnectionResetError as e: 12 print(e) 13 break 14 conn.close() 15 16 def server(): 17 server = socket.socket() 18 server.bind(('127.0.0.1',8080)) 19 server.listen(5) 20 21 while True: 22 conn,addr = server.accept() 23 print(addr) 24 tpool.submit(communicate,conn) 25 26 if __name__ == '__main__': 27 server()
View Code
客户端:
1 import socket 2 client=socket.socket() 3 client.connect(('127.0.0.1',8080)) 4 5 while True: 6 msg = input('msg>>>').strip() 7 if msg == 'q':break 8 if not msg:continue 9 client.send(msg.encode('utf-8')) 10 data = client.recv(1024) 11 print(data.decode('utf-8')) 12 13 client.close()
View Code
转载于:https://www.cnblogs.com/zahngyu/p/11360320.html
并发编程之进程池,线程池 和 异步回调,协程相关推荐
- [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors...
[Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...
- [Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ...
[Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ... 摘要 介绍 Java 并发包里的几个主要 ExecutorService . 正文 ...
- Java 并发编程——Executor框架和线程池原理
Java 并发编程系列文章 Java 并发基础--线程安全性 Java 并发编程--Callable+Future+FutureTask java 并发编程--Thread 源码重新学习 java并发 ...
- 高并发编程-自定义简易的线程池(2),体会原理
文章目录 概述 示例 概述 高并发编程-自定义简易的线程池(1),体会原理 中只实现了任务队列,我们这里把其余的几个也补充进来 拒绝策略 关闭线程池 最小 最大 活动线程数 - 示例 比较简单,直接上 ...
- java 线程钩子_高级并发编程系列六(线程池钩子函数)
1.考考你 国庆假期快要结束了,准备回到工作岗位的你,是不是已经开始撸起袖子敲代码,反正发完文章我就要准备去加班了,程序员就这样,有干劲对吧 那么来吧,让我们一起分享完高级并发编程系列中,线程池小节的 ...
- 【2021最新版】Python 并发编程实战,用多线程、多进程、多协程加速程序运行
[2021最新版]Python 并发编程实战,用多线程.多进程.多协程加速程序运行 视频地址:https://www.bilibili.com/video/BV1bK411A7tV/ P1 Pytho ...
- 并发编程(进程、线程、协程)
操作系统 一 为什么要有操作系统? 现代计算机系统是由一个或者多个处理器,主存,磁盘,打印机,键盘,鼠标显示器,网络接口以及各种其他输入输出设备组成的复杂系统,每位程序员不可能掌握所有系统实现的细节, ...
- python多线程调度_python并发编程之进程、线程、协程的调度原理(六)
进程.线程和协程的调度和运行原理总结. 系列文章 进程.线程的调度策略介绍 linux中的进程主要有三种调度策略: 优先级调度:将进程分为普通进程和实时进程: 先进先出(队列)调度:实时进程先创建的先 ...
- 高并发编程-自定义简易的线程池(1),体会原理
文章目录 概述 示例 概述 我们工作中,并发编程必然离不开jdk提供的j.u.c中的线程池 ,假设让我们自己去设计一个线程池,该从哪几个方面来着手考虑呢? 首先: 既然是线程池 , 那必然 有个初始化 ...
- winform判断线程有没有完成_并发编程系列1:线程池的架构实现、大小配置、及四种线程池使用...
△ 公众号回复关键词"架构" 即可领取<1500+BAT架构及面试专题合集> 本篇为线程池系列文章之一,不经常使用线程池的童鞋,还有对几种线程的使用不甚了解的童鞋,可以 ...
最新文章
- Python面试必备—分布式爬虫scrapy+redis解析
- 中国将对人工智能、云计算等行业独角兽IPO即报即审
- MyBatis学习总结(四)——解决字段名与实体类属性名不相同的冲突
- “蜥蜴之尾”——长老木马四代分析报告
- PHP经典文:服务容器
- android Rect类的使用
- fastapi 模式的额外信息,示例 / Cookie参数 / Header参数
- Windows 系统中常见的 Shell 命令总结(不时补充)
- ubuntu 14.04 下 安装samba 及SSH 服务端的方法
- 基于modbus协议的工业自动化网络规范_一种基于Modbus的工业通信网关设计
- 《CSS权威指南》第3版
- 性能测试工具Jmeter对数据库Mysql进行连接并压测
- S2. 口腔CT工作原理-数据采集
- 使用js+css样式实现淘宝详情页图片放大镜的效果
- 辐射定标、辐射校正、大气校正、正射校正等相关概念
- 360浏览器html位置,360浏览器的默认安装路径在哪里
- 网站ICP备案与公安备案有什么区别---kalrry
- 使用LOL盒子的数据查询简单的战斗力信息
- 写给工程师的 10 条精进原则
- 一个故事,讲懂什么是区块链
热门文章
- 单链表的增、删、查、改、python实现,超详细讲解
- 使用JQuery结合HIghcharts实现从后台获取JSON实时刷新图表
- ExtJs2.0学习系列(10)--Ext.TabPanel之第二式
- 4.3.1 jQuery基础(1)
- ASP.NET企业开发框架IsLine FrameWork系列之二--命名空间与契约
- C#开发ActiveX控件及制作CAB包
- 简单程序所需要的简单 asp.net通用数据库访问类
- Java下MySQL易用处理工具(支持XML/JavaBean)
- java怎么把数组的元素个数_想问一下怎样才可以把一个数 放在已知数组里面
- 查看远程Redis服务器的版本