python 同步与异步的性能区别及实例

同步与异步的性能区别

1.

#coding:utf-8
import gevent 
def task(pid):
    """
    Some non-deterministic task
    """
    gevent.sleep(0.5) #起到切换的作用
    print('Task %s done' % pid) 
def synchronous():
    for i in range(1,10):
        task(i) 
def asynchronous():
    threads = [gevent.spawn(task, i) for i in range(10)]
    gevent.joinall(threads) #等待所以操作都执行完毕 
print('Synchronous:')
synchronous() #同步 
print('Asynchronous:')
asynchronous()#异步  这里会按照sleep 设置来执行

2.Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。

gevent是第三方库,通过greenlet实现协程,其基本思想是:

当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。1.可用使用gevent.sleep()调整执行顺序也可以2.使用monkey patch来修改标准库,不改原始代码的方式调整。

由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:

遇到IO阻塞时会自动切换任务

#coding:utf-8
from urllib import urlopen
import gevent
from  gevent  import monkey;monkey.patch_all()  #修改标准库,使IO操作时,还会继续执行其他的协程

def t(n):
    print n
    url=urlopen(n)  #遇到IO操作都会自动执行其他协程
    urll=url.read()
    print 'len%s,url%s'%(len(urll),n)
gevent.joinall([
    gevent.spawn(t,'https://www.cnblogs.com/iexperience/p/9342446.html'),
    gevent.spawn(t,'https://www.cnblogs.com/iexperience/p/9329362.html'),
    gevent.spawn(t,'https://www.cnblogs.com/iexperience/p/9329332.html'),
    ])

结果:

https://www.cnblogs.com/iexperience/p/9342446.html

https://www.cnblogs.com/iexperience/p/9329362.html

https://www.cnblogs.com/iexperience/p/9329332.html

len10649,urlhttps://www.cnblogs.com/iexperience/p/9342446.html

len12980,urlhttps://www.cnblogs.com/iexperience/p/9329362.html

len7646,urlhttps://www.cnblogs.com/iexperience/p/9329332.html

从结果看,3个网络操作是并发执行的,而且结束顺序不同,但只有一个线程。

要让greenlet交替运行,可以通过gevent.sleep()交出控制权,像开始的实例1中的异步一样:

参考:https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001407503089986d175822da68d4d6685fbe849a0e0ca35000

通过gevent来实现单线程下的多socket并发

server 端,采用gevent协程

import sys
import socket
import time
import gevent 
from gevent import socket,monkey
monkey.patch_all()  
def server(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli)   #gevent.spwan调用handle参数并传参  
 
def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024)
            print("recv:", data)
            conn.send(data)
            if not data:             
                conn.shutdown(socket.SHUT_WR)
 
    except Exception as  ex:
        print(ex)
    finally:
        conn.close()
if __name__ == '__main__':
    server(8001)

client端

单线程的客户端

import socket
 
HOST = 'localhost'    # The remote host
PORT = 8001           # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"),encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    #print(data)
 
    print('Received', repr(data))
s.close()

多线程客户端去请求

import socket
import threading
def sock_conn():
    client = socket.socket()
    client.connect(("localhost",8001))
    count = 0
    while True:
        #msg = input(">>:").strip()
        #if len(msg) == 0:continue
        client.send( ("hello %s" %count).encode("utf-8"))

        data = client.recv(1024)

        print("[%s]recv from server:" % threading.get_ident(),data.decode()) #结果
        count +=1
    client.close()

for i in range(100):
    t = threading.Thread(target=sock_conn)
    t.start()