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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM