python 之 并发编程(线程Event、协程)
9.14 线程Event
connect线程执行到event.wait()时开始等待,直到check线程执行event.set()后立即继续线程connect
from threading import Event,current_thread,Thread import time event=Event() def check(): print('%s 正在检测服务是否正常....' %current_thread().name) time.sleep(3) event.set() def connect(): print('%s 等待连接...' %current_thread().name) event.wait() print('%s 开始连接...' % current_thread().name) if __name__ == '__main__': t1=Thread(target=connect) t2=Thread(target=connect) t3=Thread(target=connect) c1=Thread(target=check) t1.start() t2.start() t3.start() c1.start()
connect线程执行到event.wait(1)时开始等待1秒,count计数+1,如果到check线程执行event.set()前已经4秒,则终止线程connect,否则event.is_set() is True ,立即继续线程connect
from threading import Event,current_thread,Thread import time event=Event() def check(): print('%s 正在检测服务是否正常....' %current_thread().name) time.sleep(5) event.set() def connect(): count=1 while not event.is_set(): #event是否被set过,是返回True,否返回False if count == 4: print('尝试的次数过多,请稍后重试') return print('%s 尝试第%s次连接...' %(current_thread().name,count)) event.wait(1) count+=1 print('%s 开始连接...' % current_thread().name) if __name__ == '__main__': t1=Thread(target=connect) t2=Thread(target=connect) t3=Thread(target=connect) c1=Thread(target=check) t1.start() t2.start() t3.start() c1.start()
9.15 协程
协程:是单线程下的并发,又称微线程,纤程。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
-
python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到i/o或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
-
单线程内开启协程,一旦遇到i/o,就会从应用程序级别(而非操作系统)控制切换到其他任务,以此来提升效率(非i/o操作的切换与效率无关)
-
对比操作系统控制线程的切换,用户在单线程内控制协程的切换
优点:
1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2. 单线程内就可以实现并发的效果,最大限度地利用cpu
缺点:
1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
9.151 greenlet模块
from greenlet import greenlet import time def eat(name): print('%s eat 1' %name) #time.sleep(30) 遇到i/o不能自动切换 g2.switch('alex') print('%s eat 2' %name) g2.switch() def play(name): print('%s play 1' %name) g1.switch() print('%s play 2' %name) g1=greenlet(eat) g2=greenlet(play) g1.switch('egon') # egon eat 1 alex play 1 egon eat 2 alex play 2
9.152 gevent模块
import gevent def eat(name): print('%s eat 1' %name) gevent.sleep(5) #只检测gevent的i/o print('%s eat 2' %name) def play(name): print('%s play 1' %name) gevent.sleep(3) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') #异步提交任务 g2=gevent.spawn(play,'alex') # gevent.sleep(100) # g1.join() # g2.join() # joinall等待任务执行完毕再结束线程 gevent.joinall([g1,g2]) # egon eat 1 alex play 1 alex play 2 egon eat 2
from gevent import monkey;monkey.patch_all()#标记所有(包括time等)的i/o import gevent import time def eat(name): print('%s eat 1' %name) time.sleep(5) #time的i/o也可以检测 print('%s eat 2' %name) def play(name): print('%s play 1' %name) time.sleep(3) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,'alex') # gevent.sleep(100) # g1.join() # g2.join() gevent.joinall([g1,g2]) # egon eat 1 alex play 1 alex play 2 egon eat 2
验证协程的假名:
from gevent import monkey;monkey.patch_all()#标记所有time等的i/o from threading import current_thread import gevent import time def eat(): print('%s eat 1' %current_thread().name) time.sleep(5) print('%s eat 2' %current_thread().name) def play(): print('%s play 1' %current_thread().name) time.sleep(3) print('%s play 2' %current_thread().name) g1=gevent.spawn(eat) g2=gevent.spawn(play) # gevent.sleep(100) # g1.join() # g2.join() print(current_thread().name)#MainThread gevent.joinall([g1,g2]) #DummyThread-1 eat 1 DummyThread-2 play 1 DummyThread-2 play 2 DummyThread-1 eat 2
9.153 基于协程实现并发的套接字通信
服务端:
from gevent import monkey,spawn;monkey.patch_all()#标记所有time等的i/o from threading import Thread from socket import * def talk(conn): while True: try: data=conn.recv(1024) if not data:break conn.send(data.upper()) except ConnectionResetError: break conn.close() def server(ip,port,backlog=5): s = socket() s.bind((ip,port)) s.listen(backlog) while True: conn, addr = s.accept() print(addr) g=spawn(talk,conn) # 通信 s.close() if __name__ == '__main__': spawn(server,'127.0.0.1',8080).join() # server(('127.0.0.1',8080))
客户端:
from threading import Thread,current_thread from socket import * import os def client(): client = socket() client.connect(('127.0.0.1', 8080)) while True: data = '%s hello' % current_thread().name client.send(data.encode('utf-8')) res = client.recv(1024) print(res.decode('utf-8')) if __name__ == '__main__': for i in range(1000): t=Thread(target=client) t.start()