一.如何用单线程实现并发
二.线程池和进程池
三.协程
了解前提:
进程:资源单位 线程:执行单位 协程:单线程下实现并发 何为并发: 并发的条件: 多道技术 空间上的复用 时间上的复用 切换 + 保存状态 I O切换 + 保存当前状态 yield 保存上一次的结果 多进程下开多线程 多线程下再开协程 ps:并发看起来像是同时执行
可以利用我们的协程 协程实际上是不存在的是我们程序员自己意淫出来的名词为了程序员自己通过代码检测程序中的IO 一旦遇到IO输入自己通过代码进行在内部切换 让操作系统感觉是你这个线程没有任何的IO ps:欺骗操作系统 让他误以为你这个程序一直没有IO 从而保证程序在运行态和就绪态来回切换 提升代码的运行效率 补充:切换+保存状态就一定能够提升效率??? 当你的任务是IO密集型的情况下 可以提升效率
如果是任务是计算密集型 降低效率 不需要切换IO
演示代码:
客户端
# 客户请求 import socket from threading import Thread,current_thread def client1(): client = socket.socket() client.connect(('127.0.0.1', 8080)) n = 0 while True: # msg = input('输入内容:').encode('utf-8') msg = '%s %s ' % (current_thread().name, n) client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8')) n += 1 # 模拟多个客户的访问服务端 for i in range(1000): t = Thread(target=client1) t.start()
服务端
# """ 并发的效果 看上去象同时运行 1.开一个线程 2.循环协程对象 3.传参数conn 4.通讯循环 """ # 单线程下利用协程实现并发 # 将链接循环封装为函数 # 同时也将通讯循环封装为函数 # 服务端 from gevent import monkey;monkey.patch_all() import socket from gevent import spawn server = socket.socket() server.bind(("127.0.0.1",8080)) server.listen(5) # 建立半链接池 def talk(conn): # 通讯循环(封装一个函数抽出去) while True: try: data = conn.recv(1024) print(data.decode('utf-8')) conn.send(b'hello baby') except BaseException as e: print(e) break conn.close() def server1(): # 函数名和server 对象重名了 while True: # 链接循环 conn, addr = server.accept() # # 通讯循环(封装一个函数抽出去) # while True: # data = conn.recv(1024) # print(data.decode('utf-8')) # conn.send(b'hello baby') # 建立连接后 如何实现通信呢 spawn(talk, conn) # talk 自动加括号执行 if __name__ == '__main__': # 实现协程的创建 g = spawn(server1) # 依次执行完毕 g.join()
二线程池和进程池
1.线程池
""" 服务端: 24 小时不间断的服务端 只要一有人来请求访问 请求响应 固定的IP地址(网络层规定连入户联网的一台计算必须有一个点分十进制的地址) +port唯一识别一台计算上的一个应用程序) 能够支持并发 在同一时间内接受请求资源的客户端访问量 并发 看起来像同时运行 并行真正的同时运行 GIL 全局解释器锁是CPython 的特点 解释器有很多种 Jpython 由并发变成串行 虽然牺牲了效率 但事实保证了数据的安全 IO切换 网络编程 计算机和计算机的数据交互 条件: 1 物理条件 物理连接层 网线 2 数据传输需要一套标准 大家都按照规定的协议进行传输数据 规格 标准 协议 OSI七层协议 传输和解析 基于TCP和UDP通信 TCP数据的可靠性 牺牲可效率 保证数据的安全 如何传到应用 及返回请求的数据 socket帮我们封装了传输层一下的所有协议 TCP/UDP是一种协议 数据传输是基于socket套接字接口进行传输 网咯开发架构 C/S client 客户端即应用程序 计算机核心的三大硬件应用 操作系统 计算机硬件 和 server 服务端 B/S 浏览器其实本质也是C/S架构 浏览器 内部也是通过访问应用程序 只是浏览器包装了 可以帮我们传输数据通过HTTP jie 以及解析HTMl 返回数据和页面给用户 三次握手键连接 四次挥手 解除链接 """ import time from concurrent.futures import ThreadPoolExecutor from threading import Thread,current_thread import os # 先产生一个线程池对象 pool = ThreadPoolExecutor() def task(n): print('%s 当前的线程号 %s ' % (n,os.getpid()))
print(current_thread().name) # ThreadPoolExecutor-0_1
time.sleep(2) return n*2
def call_back(x): print('拿到了异步提交任务的返回结果:%s' % x.result())
print(x)
# <Future at 0x2cc39e6d080 state=finished returned int>
print(type(x))
# <class 'concurrent.futures._base.Future'>
C:Users10243AppDataLocalProgramsPythonPython36python.exe "D:/datas/day 33协程/02进程池.py"
0 当前的线程号 45568
ThreadPoolExecutor-0_0
1 当前的线程号 45568
ThreadPoolExecutor-0_1
2 当前的线程号 45568
ThreadPoolExecutor-0_2
3 当前的线程号 45568
ThreadPoolExecutor-0_3
4 当前的线程号 45568
ThreadPoolExecutor-0_4
拿到了异步提交任务的返回结果:2
5 当前的线程号 45568
ThreadPoolExecutor-0_1
拿到了异步提交任务的返回结果:4
6 当前的线程号 45568
ThreadPoolExecutor-0_2
拿到了异步提交任务的返回结果:0
7 当前的线程号 45568
ThreadPoolExecutor-0_0
拿到了异步提交任务的返回结果:6
8 当前的线程号 45568
ThreadPoolExecutor-0_3
拿到了异步提交任务的返回结果:8
9 当前的线程号 45568
ThreadPoolExecutor-0_4
拿到了异步提交任务的返回结果:10
拿到了异步提交任务的返回结果:12
拿到了异步提交任务的返回结果:16
拿到了异步提交任务的返回结果:14
拿到了异步提交任务的返回结果:18
Process finished with exit code 0
2进程池
""" 服务端: 24 小时不间断的服务端 只要一有人来请求访问 请求响应 固定的IP地址(网络层规定连入户联网的一台计算必须有一个点分十进制的地址) +port唯一识别一台计算上的一个应用程序) 能够支持并发 在同一时间内接受请求资源的客户端访问量 并发 看起来像同时运行 并行真正的同时运行 GIL 全局解释器锁是CPython 的特点 解释器有很多种 Jpython 由并发变成串行 虽然牺牲了效率 但事实保证了数据的安全 IO切换 网络编程 计算机和计算机的数据交互 条件: 1 物理条件 物理连接层 网线 2 数据传输需要一套标准 大家都按照规定的协议进行传输数据 规格 标准 协议 OSI七层协议 传输和解析 基于TCP和UDP通信 TCP数据的可靠性 牺牲可效率 保证数据的安全 如何传到应用 及返回请求的数据 socket帮我们封装了传输层一下的所有协议 TCP/UDP是一种协议 数据传输是基于socket套接字接口进行传输 网咯开发架构 C/S client 客户端即应用程序 计算机核心的三大硬件应用 操作系统 计算机硬件 和 server 服务端 B/S 浏览器其实本质也是C/S架构 浏览器 内部也是通过访问应用程序 只是浏览器包装了 可以帮我们传输数据通过HTTP jie 以及解析HTMl 返回数据和页面给用户 三次握手键连接 四次挥手 解除链接 """ import time from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor from threading import Thread,current_thread import os # 先产生一个线程池对象 # pool = ThreadPoolExecutor() pool = ProcessPoolExecutor(5) # """ 池子中个数创建的进程/线程创建一次就不会再创建 一次连接5个 再多一个先等着 自始至终都是那几个 这样的话节省了返回开辟进程/线程的资源 """ def task(n): print('%s 当前的进程号 %s ' % (n,os.getpid())) time.sleep(2) return n*2 def call_back(x): print('拿到了异步提交任务的返回结果:%s' % x.result()) # print(x) # <Future at 0x2cc39e6d080 state=finished returned int> # print(type(x)) # <class 'concurrent.futures._base.Future'> # 开启进程池 if __name__ == '__main__': t_list = [] for i in range(10): # print('111') # def submit(self, fn, *args, **kwargs): # with self._shutdown_lock: # if self._shutdown: # raise RuntimeError('cannot schedule new futures after shutdown') # # f = _base.Future() res = pool.submit(task,i).add_done_callback(call_back) # b别加括号 # print(res,'xixi') # print('222') t_list.append(res) # print(t_list,'haha') C:Users10243AppDataLocalProgramsPythonPython36python.exe "D:/datas/day 33协程/02进程池.py" 0 当前的进程号 44220 1 当前的进程号 41688 2 当前的进程号 45332 3 当前的进程号 45916 4 当前的进程号 45200 5 当前的进程号 44220 拿到了异步提交任务的返回结果:0 拿到了异步提交任务的返回结果:2 6 当前的进程号 41688 7 当前的进程号 45332 拿到了异步提交任务的返回结果:4 8 当前的进程号 45200 9 当前的进程号 45916 拿到了异步提交任务的返回结果:6 拿到了异步提交任务的返回结果:8 拿到了异步提交任务的返回结果:12 拿到了异步提交任务的返回结果:10 拿到了异步提交任务的返回结果:14 拿到了异步提交任务的返回结果:16 拿到了异步提交任务的返回结果:18 Process finished with exit code 0
三协程
""" yield 虽然能够保存上次执行的结果的状态 但是没有办法识别IO 所以需要找一个既可以保存运行的状态有能 识别捕获IO进行内部切换直到 执行完毕任务 提高效益 单线程的下的并发 协程导入gevent 模块 但是 需要导入monkey 才能识别IO """ import time from gevent import monkey;monkey.patch_all() # 可以写在一行 from gevent import spawn """ 注意gevent模块没办法自动识别time.sleep()等IO 情况 需要手动再配置一个参数monkey """ # 定义几个函数 def f1(): print('heihei') time.sleep(3) # 会根据最长的时间执行程序 print('heihei') def f2(): print('啊哈哈') time.sleep(2) print('啊哈哈') def f3(): print('嘻嘻嘻') time.sleep(1) print('嘻嘻嘻') start_time = time.time() g1 = spawn(f1) g2 = spawn(f2) g3 = spawn(f3) g1.join() g2.join() g3.join() print(time.time()-start_time) # heihei # 啊哈哈 # 嘻嘻嘻 # 嘻嘻嘻 # 啊哈哈 # heihei # 3.004105567932129