5,线程池,进程池,协程,IO模型
Posted fzhiyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5,线程池,进程池,协程,IO模型相关的知识,希望对你有一定的参考价值。
今日内容:
1,线程池
2,进程池
3,协程
4,IO 模型
服务端要满足这三个条件:
1,24小时不间断的提供服务
2,能够支持高并发
3,要有固定的IP地址和端口
在服务端这个地方会出现阻塞态情况:
阻塞IO 操作有:
1,链接循环
2,通信循环
单线程实现高并发思路:
为了更好的提高程序的运行效率,即实现高并发,让服务端同时能够接受多个客户端的消息
所以一般在服务端会把,连接循环和通信循环封装为两个不同的函数方法,
这样当一个客户端与服务端进行通信时,服务端的连接循环可以和其他客户端进行连接,
不要等待,从而提高了效率,可以利用单线程来完成高并发,即来一个客户端就开一个线程,
每来一个客户端就开一个线程,看上去像高并发!
案列:
import socket
from threading import Thread
def server1():
server = socket.socket()
server.bind((‘127.0.0.1‘, 1688))
server.listen(5)
server2(server)
def task(conn): 通信循环
while True:
try:
data = conn.recv(1024) # 在这里会形成阻塞,等待对客户端发消息过来
# 不加,就会形成阻塞,一直等待对方发消息过来
if len(data) == 0:break # 在这里最好把这个条件判断加上,以适用不同的操作系统
print(data.decode(‘utf-8‘))
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break
conn.close()
def server2(server): 连接循环
while True:
conn, addr = server.accept() # 在这里会形成阻塞,监听是否有客户端过来
t = Thread(target=task, args=(conn,))
t.start()
问题:上面的案例虽然解决了线程高并发的问题,但是但有大量的客户端来访问服务端时,由于开启线程也是需要消耗资源的
所以但有几亿个用户同时访问时,也架不住量大,也会造成内存溢出,服务器奔溃现象,这是由于硬件性能决定的,
所以为了防止服务器奔溃,保证数据的安全性,在保证计算机能够承受的范围内,最大限度的利用计算机,
就引入了池这个概念
1,什么是池?
在保证计算机硬件安全的情况下最大限度的利用计算机
池其实是降低了程序的运行效率 但是保证了计算机硬件的安全
(硬件的发展跟不上软件的速度)
2,线程池,
进程池
案列:
import time
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
# 线程池,在你不传参数的情况下,默认的是当前计算机CPU的个数*5
pool = ThreadPoolExecutor(5) # 括号内可以传参数,指定线程池的线程个数
# 只要创建了五个线程,就永远是这五个线程,这五个线程并不会动态创建,动态销毁
# 好处是节省空间,进程也是如此
def task(n):
print(n)
time.sleep(2)
#return n*2
t_list = []
for i in range(20):
res = pool.submit(task,i) # submit 的返回值是一个内部Future类产生的对象
print(res) # 类对象 res.result()它拿到的结果就是task任务的返回值
print(res.result()) # 结果为 0 None 1 None 2 None 3 None...19 None
1,将结果变为串行 2,结果为None (在这里res.result()拿到得是task这个任务的返回结果
原地等待任务的返回结果 ,即同步,如果这样调就会将并发变为串行
结果为None,是因为task任务的返回结果为None,所以为空,如果加上return n*2,那就不为空
t_list.append(res)
pool.shutdown() # 1,关闭池子,即20个线程提交完毕,立马关闭池子,就像关门打狗一样
# 2,等待池子里面所有的任务完成之后才会继续向下执行
for p in t_list:
print(‘>>>>>对象‘,p.result(),p)
# 结果是乱的:14
# >>>>>对象 None <Future at 0x2aa9829a5f8 state=finished returned NoneType>
如果加上pool.shutdown(),就是等任务全部结束,才会走pool.shutdown() 下面的代码,
3,进程池
from concurrent.futures import ProcessPoolExecutor
pool = ProcessPoolExecutor(5) #进程池不传参数,默认当前CPU的个数
#异步回调机制:当异步提交的任务有返回结果之后,会自动触发回调函数的执行
4,协程:
1,什么是协程:单线程下实现并发 只是一个名称,即表示一个效果
进程:正在运行的程序,资源单位
线程:cpu最小的执行单位
并发:看起来像同时进行,就可以称之为并发,即切换+保存状态
并行:真正的同时进行,单核情况下无法并行
多道技术:1,空间上的复用 即 多个程序利用一台计算机
2,时间上的复用 切换+保存状态
切换的状态:1,程序遇到IO操作,就会进行切换
2,长时间的占用CPU 时,时间片用完,也会切换
程序员自己通过代码自己检测程序中的IO
一旦遇到IO自己通过代码切换
给操作系统的感觉是你这个线程没有任何的IO
ps:欺骗操作系统 让它误认为你这个程序一直没有IO
从而保证程序在运行态和就绪态来回切换
提升代码的运行效率
gevent模块
import time
from gevent import monkey;monkey.patch_all() #固定写法
from gevent import spawn
# 注意gevent模块没办法自动识别time.sleep()等io情况
# 需要你手动在配置一个参数,自动检测io,遇到io就开始切换
def heng():
print(‘哼22‘)
time.sleep(1)
print(‘哈哈哈我‘)
def ao():
print(‘嗷嗷‘)
time.sleep(3)
print(‘收拾一下‘)
start = time.time() # 在这里注意spawn()是有返回值的,返回值是一个类的对象
g1 = spawn(heng) # spawn ,相当于一个列表,先来一个heng添加到列表中,后面再来一个ao在添加到列表中
g2 = spawn(ao) # 一旦heng遇到IO 就执行ao,一旦ao遇到IO就立即执行heng,就是不断来回切换
g1.join() # 等待任务完成,不然就不会执行此任务,而是走主线程,主线程没代码,程序直接结束
g2.join() # 在右键运行后,spawn就在这两个任务之间来回疯狂的切,这个切换是在代码层面的切
# 来欺骗操作系统,让操作系统误认为没有遇到IO
# heng() # 串行执行
# ao() # 串行执行 这样执行的结果是5秒多
print(time.time()- start) # spawn(heng) spawn(ao)执行的结果
5,IO模型
数据交互,要的数据都在内存,是互相把数据发到对方的内存
1,阻塞IO
先系统要数据,没有数据时,阻塞+等待数据,一旦有数据来了
进行数据的拷贝,拷贝到数据在回复ok,中间等待和拷贝的过程都是阻塞IO
2, 非阻塞IO
程序处于运行态和就绪态,向内存要数据,没有数据也会立马给你返回一个结果
一直反复的要,一直反复的回,直到有数据真的来了,就会进行拷贝,此时会形成阻塞
这就是非阻塞io
3, 异步IO
异步进行,异步提交任务后,立即给你一消息,但是内部在等待,等到内部有数据是立马
返回给你,进行下一步操作
4, IO多路复用
就是 select,相当于一个检测机制,像一个列表,统一管理,要什么数据select去负责跟
内存要数据,由他负责,不用你管,有了之后回来给你,然后再执行以下操作,相当于餐厅服务员
以上是关于5,线程池,进程池,协程,IO模型的主要内容,如果未能解决你的问题,请参考以下文章