欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-08-20 14:07:19
9.14 线程Event connect线程执行到event.wait()时开始等待,直到check线程执行event.set()后立即继续线程connect connect线程执行到event.wait(1)时开始等待1秒,count计数+1,如果到check线程执行event.set()前已经4 ......

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 协程

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

  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

验证协程的假名:

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()