标签:bin 参数 知识点 形式 code 并发编程 exce 用户 sele
我们知道一个线程同一时间内只能被操作系统分配一个CPU资源, 我们可以基于多进程实现并发, 也可以基于多线程实现并发, CPU正在运行一个任务, 有两种情况下会被切去执行其它任务, 一种是该任务发生了阻塞, 另一是该任务运行时间过长或者被其他优先级更高的任务夺走CPU, 对于单线程来说, 如果一个单线程下运行了多个任务, 那么就不可避免的出现I/O操作, 一旦一个任务出现阻塞的情况, 那么整个线程将处于阻塞状态(因为一旦阻塞, CPU资源将会被夺走), 但是如果我们能在自己的应用程序中(即用户级别, 非操作系统级别)控制单线程下多个任务能在一个任务遇到I/O之后立马切换到另一个任务, 那么我们就可以保证该线程能最大限度的保持就绪状态(也就是随时能进入运行态), 相当于我们在应用程序级别将I/O操作隐藏起来, 迷惑操作系统, 让其看到的状态就是一直在计算, I/O比较少, 于是操作系统就会将更多的CPU资源分配给该线程
import time
def Foo():
for i in range(100):
print(f"--->Foo:{i}")
yield # 保存状态
def Bar():
f = Foo()
for i in range(10000000):
i += 1
next(f)
start_time = time.time()
Bar()
stop_time = time.time()
print(f"user time:{stop_time-start_time}")
ps : yield无法检测I/O, 无法实现遇到I/O就进行切换
ps : 如何实现自动检测 I/O, 上面模拟使用的 yield 以及 greenlet 都无法做到, 于是以下就开始介绍 gevent 模块(select机制)
??"cmd" 或 "pycharm" 的 "Terminal"
pip3 install gevent
Gevent是Python的第三方库, 它为各种并发和网络相关的任务提供了整洁的API, 我们可以通过gevent轻松实现并发同步或异步编程
在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程
Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度
方法 | 作用 |
---|---|
gevent.spawn(func,args/kwargs) | 创建一个协程对象, 第一个参数是函数名, 后面的参数是函数的位置参数或者关键字参数 |
[协程对象].join( ) | 等待协程对象的结束 |
gevent.joinall([对象1,对象2]) | 等待多个协程对象的结束, 参数是一个列表, 放多个协程对象 |
[协程对象].value | 拿到协程对象的返回值 |
import gevent
import time
def eat(name):
print(f"{name}正在吃东西")
gevent.sleep(3)
print(f"{name}吃完了")
return "i am eat"
def play(name):
print(f"{name}正在玩手机")
gevent.sleep(1)
print(f"{name}玩够了手机")
return "i am play"
start_time = time.time()
g1 = gevent.spawn(eat,"派大星")
g2 = gevent.spawn(play,"海绵宝宝")
# g1.join() # 等待协程对象g1结束
# g2.join() # 等待协程对象g2结束
gevent.joinall([g1,g2]) # 等待协程对象g1和g2结束
print(g1.value) # 获取协程对象g1的返回值
print(g2.value) # 获取协程对象g2的返回值
print(f"用时:{time.time()-start_time}")
‘‘‘输出
派大星正在吃东西
海绵宝宝正在玩手机
海绵宝宝玩够了手机
派大星吃完了
i am eat
i am play
用时:3.0330262184143066
‘‘‘
像time.sleep(2)
或者其他类型的I/O, gevent模块无法识别, 只能识别 gevent.sleep(2)
解决方法 : 使用猴子补丁让其能识别 from gevent import monkey;monkey.patch_all()
,放在文件开头
from gevent import monkey;monkey.patch_all()
import gevent
import time
from threading import current_thread
def eat(name):
print(current_thread().name) # 查看该线程的名字
print(f"{name}正在吃东西")
time.sleep(3)
print(f"{name}吃完了")
return "i am eat"
def drink(name):
print(current_thread().name) # 查看该线程的名字
print(f"{name}正在喝汤")
time.sleep(2)
print(f"{name}把汤喝完了")
return "i am drink"
def play(name):
print(current_thread().name) # 查看该线程的名字
print(f"{name}正在玩手机")
time.sleep(1)
print(f"{name}玩够了手机")
return "i am play"
start_time = time.time()
g1 = gevent.spawn(eat,"派大星")
g2 = gevent.spawn(drink,"章鱼哥")
g3 = gevent.spawn(play,"海绵宝宝")
# g1.join() # 等待协程对象g1结束
# g2.join() # 等待协程对象g2结束
# g3.join() # 等待协程对象g3结束
gevent.joinall([g1,g2,g3]) # 等待协程对象g1和g2结束
print(g1.value) # 获取协程对象g1的返回值
print(g2.value) # 获取协程对象g2的返回值
print(g3.value) # 获取协程对象g3的返回值
print(f"用时:{time.time()-start_time}")
‘‘‘输出
Dummy-1
派大星正在吃东西
Dummy-2
章鱼哥正在喝汤
Dummy-3
海绵宝宝正在玩手机
海绵宝宝玩够了手机
章鱼哥把汤喝完了
派大星吃完了
i am eat
i am drink
i am play
用时:3.0230190753936768
‘‘‘
??# 可以查看到三个线程的名字 : Dummy-1、Dummy-2、Dummy-3、(都是假线程)
from gevent import monkey;monkey.patch_all() # 添加猴子补丁
import gevent
from socket import *
# 建链接循环
def link(ip,port):
try:
server = socket(AF_INET, SOCK_STREAM)
server.bind((ip,port))
server.listen(5)
except Exception as E:
print(E);return
while 1:
conn,addr = server.accept()
gevent.spawn(communication,conn)
# 建立链接成功之后开启一个协程任务进行通信循环
# 通信循环
def communication(conn):
while 1:
try:
data = conn.recv(1024)
if len(data) == 0:break
conn.send(data.upper())
except Exception as E:
print(E);break
conn.close()
if __name__ == ‘__main__‘:
g1 = gevent.spawn(link,"127.0.0.1",8090) # 先启动一个建立链接循环的协程任务
g1.join()
from socket import *
client = socket(AF_INET,SOCK_STREAM)
client.connect(("127.0.0.1",8090))
while 1:
user = input(">>").strip()
if len(user) == 0:continue
client.send(user.encode("utf-8"))
data = client.recv(1024)
print(data.decode("utf-8"))
from threading import Thread,current_thread
from socket import *
def connection(ip,port,i):
client = socket(AF_INET,SOCK_STREAM)
client.connect((ip,port))
while 1:
client.send(f"客户端编号:{i},名字:{current_thread().name}".encode("utf-8"))
data = client.recv(1024)
print(data.decode("utf-8"))
if __name__ == ‘__main__‘:
for i in range(10): # 多线程开启 10 个客户端进行与服务端的连接
t = Thread(target=connection,args=("127.0.0.1",8090,i))
t.start()
标签:bin 参数 知识点 形式 code 并发编程 exce 用户 sele
原文地址:https://www.cnblogs.com/songhaixing/p/14342143.html