python 之 并发编程(线程Event、协程)

时间:2021-11-11 23:35:24

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

python 之 并发编程(线程Event、协程)python 之 并发编程(线程Event、协程)
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()
View Code

9.15 协程

协程:是单线程下的并发,又称微线程,纤程。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

  1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到i/o或执行时间过长就会*交出cpu执行权限,切换其他线程运行)

  2. 单线程内开启协程,一旦遇到i/o,就会从应用程序级别(而非操作系统)控制切换到其他任务,以此来提升效率(非i/o操作的切换与效率无关)

  3. 对比操作系统控制线程的切换,用户在单线程内控制协程的切换

  优点:
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

验证协程的假名:

python 之 并发编程(线程Event、协程)python 之 并发编程(线程Event、协程)
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
View Code

9.153 基于协程实现并发的套接字通信

服务端:

python 之 并发编程(线程Event、协程)python 之 并发编程(线程Event、协程)
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))
View Code

客户端:

python 之 并发编程(线程Event、协程)python 之 并发编程(线程Event、协程)
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()
View Code