Python-并发编程(协程)

使用gevent模块实现并发

例子

服务器
from gevent import monkey#往常扩展模块要在内置模块下面,但是这个扩展模块必须写下上面
monkey.patch_all()
import gevent
import socket

def talk():
    while True:
        conn.send(b'helle')
        msg = conn.recv(1024)
        print(msg)
    conn.close()
sk=socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
while True:
    conn,addr = sk.accept()
    gevent.spawn(talk,conn)#实现并发
sk.close()
#=====================
客户端
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9000))

while True:
    msg=sk.recv(1024)
    print(msg)
    ret = input('>>>').encode('utf-8')
    sk.send(ret)
sk.close()

协程

# 协程和线程有什么关系?
#协程是线程内的一个单位
#线程已经是CPU调度的最小单位了
#协程--又叫纤程
#Cpython解释器下的多线程,在同一时刻只能有一个线程访问CPU
#所以多线程能发挥显著作用的时候就是不太耗CPU,涉及到的计算操作少
#IO高,大部分时间都用再等待上

#协程的本质是一条线程的一部分---下图是解释什么是协程,就是相当于是并发的样子,两个线程

#线程: #正常的线程,遇到阻塞就停下来,知道阻塞事件结束,才继续 # 协程: #利用了协程,就把线程分成了好几段,在一个任务出现阻塞的时候,自动的切换到 #另一个任务去执行,这些所有的事在一个线程里面完成。 #如果这个程序从头到尾没有IO,没有阻塞,程序根本就不会在多个任务之间切换 #就是一个顺序执行的,协程对于高计算型的代码没有用 #对于高IO型 #线程 #是计算机中最小的CPU调度单位 #协程 # --不用考虑数据安全,因为就在一个线程上执行 # --协程是如何调度的? #协程不能被操作系统调度 #是用户级别来调度的,就是gevent调度的 #协程的调度速度比线程还快 #协程的调度由于是Python代码级别的而不是操作系统级别的,所以用户的可控制性更强,降低了操作系统的工作量

协程到底是怎么实现程序之间的切换的

#生成器
def func(): #func是一个任务
    print(123)
    yield 1
    print(456)
    yield 2
    print(789)
    yield 3
g =func()
print(g.__next__())#程序之间的切换,代码会记住执行的位置,等待下一次执行

def wahaha(g):#wahaha是一个任务
    for i in g:
        print(i)
g = func()
wahaha(g)
#两个任务交替执行---说明程序之间的切换是可以控制的
import greenlet
#两个模块gevent,greenLet
#gevent是依赖greenLet 协程之间的切换是由greenLet完成的
from greenlet import greenlet

def eat(name):
    print('%s eat 1' %name)
    g2.switch('egon')
    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')#可以在第一次switch时传入参数,以后都不需要

#程序级别的切换

  #切换并不能让你的程序效率提高,反而会下降

#如果遇到IO才切换,那么就能提高效率,这个IO切换是由gevent借助greenLet来实现的

import gevent#能够在遇到自己认识的io操作的时候,主动调用greenLet中的switch来切换到其他的任务已提高程序的效率
def eat(name):
    print('%s eat 1' %name)
    gevent.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    gevent.sleep(1)
    print('%s play 2' %name)


g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('')

如果吧.join()取消,那么只会打印出‘主’

import gevent#能够在遇到自己认识的io操作的时候,主动调用greenLet中的switch来切换到其他的任务已提高程序的效率
def eat(name):
    print('%s eat 1' %name)
    gevent.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    gevent.sleep(1)
    print('%s play 2' %name)


g1=gevent.spawn(eat,'egon') #就是发布了任务
g2=gevent.spawn(play,name='egon')#就是发布了任务    
gevent.sleep(1) #使用这个睡1S,能够打印出部分
#g1.join() #等待g1任务执行完毕,阻塞,帮助你完成g1中函数的全部内容
#g2.join()
#或者gevent.joinall([g1,g2]) #这个可以实现上面两个.join()的功能
print('')

gevent并不认识不在这个模块中的IO操作time.sleep(2)

import time
import gevent
def eat(name):
    print('%s eat 1' %name)
    time.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    time.sleep(1)
    print('%s play 2' %name)
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('')

解决办法

from gevent import monkey
monkey.patch_all()#这个作用就是将gevent模块之外的sleep归为我认识的IO操作,就能认识time模块中的sleep了
import time
import gevent
def eat(name):
    print('%s eat 1' %name)
    time.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    time.sleep(1)
    print('%s play 2' %name)
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('')

验证确定是否是协程

from gevent import monkey
monkey.patch_all()#这个作用就是将gevent模块之外的sleep归为我认识的IO操作,就能认识time模块中的sleep了
import time
import gevent
from threading import currentThread
def eat(name):
    print(currentThread())
    print('%s eat 1' %name)
    time.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print(currentThread())
    print('%s play 1' %name)
    time.sleep(1)
    print('%s play 2' %name)
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('')

如何解决并发问题

#解决并发问题??
#多进程   高计算型,浪费操作系统和资源,可以利用多核
#多线程  高IO型,也会给操作系统添加负担,会占用比进程少的资源
            #受到GIL的影响
            # 不能利用多核
#协程    高IO型,完全不会给操作系统添加负担,几乎不占资源
            #始终不能利用多核

#多进程+多线程 :建议不超过100条
#多进程+多线程+协程 :最多接收50000条,这个是多快的
    #假设是4*CPU
    #多进程 CPU+1=5
    #多线程 CPU*5 = 20
    #协程   500个协程

验证协程是否能执行500个请求---使用协程接受500个并发

#服务器
from gevent import monkey#往常扩展模块要在内置模块下面,但是这个扩展模块必须写下上面
monkey.patch_all()
import gevent
import socket

def talk():
    while True:
        conn.send(b'helle')
        msg = conn.recv(1024)
        print(msg)
    conn.close()
sk=socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
while True:
    conn,addr = sk.accept()
    gevent.spawn(talk,conn)#实现并发
sk.close()
#客户端
import socket
from threading import Thread
def client():
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))

    while True:
        msg=sk.recv(1024)
        print(msg)
        ret = input('>>>').encode('utf-8')
        sk.send(ret)
        sk.close()
if __name__ == '__main__':
    for i in range(500):
        Thread(target=client).start()
 
原文地址:https://www.cnblogs.com/xiao-xuan-feng/p/14420713.html