python並發編程之協程

來源:互聯網
上載者:User

標籤:thread   檢測   操作   一行代碼   print   color   time   decode   ons   

參考資料:http://www.cnblogs.com/Eva-J/articles/8324673.html

協程介紹

協程:是單線程下的並發,又稱微線程,纖程。英文名Coroutine。一句話說明什麼是線程:協程是一種使用者態的輕量級線程,即協程是由使用者程式自己控制調度的。、

需要強調的是:

#1. python的線程屬於核心層級的,即由作業系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行許可權,切換其他線程運行)#2. 單線程內開啟協程,一旦遇到io,就會從應用程式層級(而非作業系統)控制切換,以此來提升效率(!!!非io操作的切換與效率無關)

對比作業系統控制線程的切換,使用者在單線程內控制協程的切換

優點如下:

#1. 協程的切換開銷更小,屬於程式層級的切換,作業系統完全感知不到,因而更加輕量級#2. 單線程內就可以實現並發的效果,最大限度地利用cpu

缺點如下:

#1. 協程的本質是單線程下,無法利用多核,可以是一個程式開啟多個進程,每個進程內開啟多個線程,每個線程內開啟協程#2. 協程指的是單個線程,因而一旦協程出現阻塞,將會阻塞整個線程

總結協程特點:

  1. 必須在只有一個單線程裡實現並發
  2. 修改共用資料不需加鎖
  3. 使用者程式裡自己儲存多個控制流程的上下文棧
  4. 附加:一個協程遇到IO操作自動切換到其它協程(如何?檢測IO,yield、greenlet都無法實現,就用到了gevent模組(select機制))
Greenlet模組

安裝 :pip3 install greenlet

g1=gevent.spawn(func,1,,2,3,x=4,y=5)建立一個協程對象g1,spawn括弧內第一個參數是函數名,如eat,後面可以有多個參數,可以是位置實參或關鍵字實參,都是傳給函數eat的g2=gevent.spawn(func2)g1.join() #等待g1結束g2.join() #等待g2結束#或者上述兩步合作一步:gevent.joinall([g1,g2])g1.value#拿到func1的傳回值
用法介紹
import geventdef 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(‘主‘)
例:遇到io主動切換

上例gevent.sleep(2)類比的是gevent可以識別的io阻塞,而time.sleep(2)或其他的阻塞,gevent是不能直接識別的需要用下面一行代碼,打補丁,就可以識別了

from gevent import monkey;monkey.patch_all()必須放到被打補丁者的前面,如time,socket模組之前

或者我們乾脆記憶成:要用gevent,需要將from gevent import monkey;monkey.patch_all()放到檔案的開頭

from gevent import monkey;monkey.patch_all()import geventimport timedef eat():    print(‘eat food 1‘)    time.sleep(2)    print(‘eat food 2‘)def play():    print(‘play 1‘)    time.sleep(1)    print(‘play 2‘)g1=gevent.spawn(eat)g2=gevent.spawn(play)gevent.joinall([g1,g2])print(‘主‘)
View Code

我們可以用threading.current_thread().getName()來查看每個g1和g2,查看的結果為DummyThread-n,即假線程

from gevent import monkey;monkey.patch_all()import threadingimport geventimport timedef eat():    print(threading.current_thread().getName())    print(‘eat food 1‘)    time.sleep(2)    print(‘eat food 2‘)def play():    print(threading.current_thread().getName())    print(‘play 1‘)    time.sleep(1)    print(‘play 2‘)g1=gevent.spawn(eat)g2=gevent.spawn(play)gevent.joinall([g1,g2])print(‘主‘)
查看threading.current_thread().getName()
from gevent import spawn,joinall,monkey;monkey.patch_all()import timedef task(pid):    """    Some non-deterministic task    """    time.sleep(0.5)    print(‘Task %s done‘ % pid)def synchronous():  # 同步    for i in range(10):        task(i)def asynchronous(): # 非同步    g_l=[spawn(task,i) for i in range(10)]    joinall(g_l)    print(‘DONE‘)    if __name__ == ‘__main__‘:    print(‘Synchronous:‘)    synchronous()    print(‘Asynchronous:‘)    asynchronous()#  上面程式的重要部分是將task函數封裝到Greenlet內部線程的gevent.spawn。#  初始化的greenlet列表存放在數組threads中,此數組被傳給gevent.joinall 函數,#  後者阻塞當前流程,並執行所有給定的greenlet任務。執行流程只會在 所有greenlet執行完後才會繼續向下走。
Gevent之同步與非同步Gevent之應用舉例一
from gevent import monkey;monkey.patch_all()import geventimport requestsimport timedef get_page(url):    print(‘GET: %s‘ %url)    response=requests.get(url)    if response.status_code == 200:        print(‘%d bytes received from %s‘ %(len(response.text),url))start_time=time.time()gevent.joinall([    gevent.spawn(get_page,‘https://www.python.org/‘),    gevent.spawn(get_page,‘https://www.yahoo.com/‘),    gevent.spawn(get_page,‘https://github.com/‘),])stop_time=time.time()print(‘run time is %s‘ %(stop_time-start_time))
協程應用:爬蟲Gevent之應用舉例二

通過gevent實現單線程下的socket並發

注意 :from gevent import monkey;monkey.patch_all()一定要放到匯入socket模組之前,否則gevent無法識別socket的阻塞

from gevent import monkey;monkey.patch_all()from socket import *import gevent#如果不想用money.patch_all()打補丁,可以用gevent內建的socket# from gevent import socket# s=socket.socket()def server(server_ip,port):    s=socket(AF_INET,SOCK_STREAM)    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)    s.bind((server_ip,port))    s.listen(5)    while True:        conn,addr=s.accept()        gevent.spawn(talk,conn,addr)def talk(conn,addr):    try:        while True:            res=conn.recv(1024)            print(‘client %s:%s msg: %s‘ %(addr[0],addr[1],res))            conn.send(res.upper())    except Exception as e:        print(e)    finally:        conn.close()if __name__ == ‘__main__‘:    server(‘127.0.0.1‘,8080)
server
from socket import *client=socket(AF_INET,SOCK_STREAM)client.connect((‘127.0.0.1‘,8080))while True:    msg=input(‘>>: ‘).strip()    if not msg:continue    client.send(msg.encode(‘utf-8‘))    msg=client.recv(1024)    print(msg.decode(‘utf-8‘))
client
from threading import Threadfrom socket import *import threadingdef client(server_ip,port):    c=socket(AF_INET,SOCK_STREAM) #通訊端對象一定要加到函數內,即局部名稱空間內,放在函數外則被所有線程共用,則大家公用一個通訊端對象,那麼用戶端連接埠永遠一樣了    c.connect((server_ip,port))    count=0    while True:        c.send((‘%s say hello %s‘ %(threading.current_thread().getName(),count)).encode(‘utf-8‘))        msg=c.recv(1024)        print(msg.decode(‘utf-8‘))        count+=1if __name__ == ‘__main__‘:    for i in range(500):        t=Thread(target=client,args=(‘127.0.0.1‘,8080))        t.start()
多線程並發多個用戶端

 

python並發編程之協程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.