并发编程之 协程

协程 (单线程下实现并发)

  进程:资源单位

  线程:执行单位

  协程:单线程下实现并发

并发:切换+保存状态

程序员自己通过代码自己检测程序中的IO

一旦遇到了IO自己通过代码切换

给操作系统的感觉就是你这个线程没有任何的IO    从而提升代码的运行效率

 

切换+保存状态一定能够提升效率吗?

  1.当任务是IO密集型的情况下     提升效率

     2.当任务是计算密集型的情况下    降低效率

 

接下来 我们进行验证  

1.在计算密集型的情况下,通过切换+保存状态   效率到底是降低了还是提升了呢?

这是串行执行的时间:

# 串行执行 0.8540799617767334
import time

def func1():
    for i in range(10000000):
        i+1

def func2():
    for i in range(10000000):
        i+1

start = time.time()
func1()
func2()
stop = time.time()
print(stop - start)

下面是切换+保存状态的执行时间:

#基于yield并发执行  1.3952205181121826
import time
def func1():
    while True:
        10000000+1
        yield

def func2():
    g=func1()
    for i in range(10000000):
        time.sleep(100)  # 模拟IO,yield并不会捕捉到并自动切换
        i+1
        next(g)

start=time.time()
func2()
stop=time.time()
print(stop-start)

根据执行时间来看  明显效率是降低了

2.在IO密集型的情况下,通过切换+保存状态   效率到底是降低了还是提升了呢?

首先我们需要找到一个能够识别IO的工具,遇到IO就可以自动的切换

这里我们就用到了gevent模块

这是串行的执行结果:

 

from gevent import monkey;monkey.patch_all()
from gevent import spawn
import  time

'''
注意  gevent模块没办法自动识别 time.sleep 等io情况
需要你手动配置一个参数
'''

def heng():
    print('哼!')
    time.sleep(2)
    print('哼!')

def ha():
    print('哈!')
    time.sleep(3)
    print('哈!')

start = time.time()

heng()
ha()

print(time.time()-start)
#5.041796445846558

 

下面是通过切换+保存状态来执行  利用gevent模块

from gevent import monkey;monkey.patch_all()
from gevent import spawn
import  time

'''
注意  gevent模块没办法自动识别 time.sleep 等io情况
需要你手动配置一个参数
'''

def heng():
    print('哼!')
    time.sleep(2)
    print('哼!')

def ha():
    print('哈!')
    time.sleep(3)
    print('哈!')

start = time.time()

g= spawn(heng)
g1 = spawn(ha)
g.join()
g1.join()

print(time.time()-start)

#3.041301727294922

明显的看出在IO密集型的情况下  切换+保存状态能够提升效率

 

Gevent应用举例 

利用协程实现并发

实现TCP服务端的并发

server端

import socket
from gevent import monkey;monkey.patch_all()
from gevent import spawn

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.listen()
def talk(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0 :break
            print(data.decode('utf-8'))
            conn.send(b'hi')
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()

def server():
    while True:
        conn,addr = sk.accept()
        spawn(talk,conn)

if __name__ == '__main__':
    res = spawn(server)
    res.join()

client端 起了400个线程

from threading import Thread
import socket

def client():
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))


    while True:
        sk.send(b'hello')
        data = sk.recv(1024).decode('utf-8')
        print(data)

for i in range(400):
    t = Thread(target=client)
    t.start()

 

上一篇:165 python网络编程 - 单进程服务器(gevent版)


下一篇:python-如何在WSGI处理程序中捕获“ [Errno 32]损坏的管道”