學習筆記Python篇~協程

協程概念

  • 協程:是單線程下的併發,又稱微線程。
  • 協程是一種用戶態的輕量級線程,即協程是由用戶程序自己控制調度的。
  • 優點
    • 1、協程的切換開銷更小,屬於程序級別的切換,操作系統完全感知不到,因而更加輕量級
    • 2、單線程內就可以實現併發的效果,最大限度地利用cpu
  • 缺點
    • 1、協程的本質是單線程下,無法利用多核,可以是一個程序開啟多個進程,每個進程內開啟多個線程,每個線程內開啟協程
    • 2、 協程指的是單個線程,因而一旦協程出現阻塞,將會阻塞整個線程
  • 特點
    • 1、必須在只有一個單線程裡實現併發
    • 2、修改共享數據不需加鎖
    • 3、用戶程序裡自己保存多個控制流的上下文棧
    • 4、 附加:一個協程遇到IO操作自動切換到其它協程(如何實現檢測IO,yield、greenlet都無法實現,就用到了gevent模塊(select機制))

    greenlet

    • 需要安裝第三方模塊:pip install greenlet
    • 生成器實現協程非常不方便,尤其很多協程的時候,使用greenlet就要方便很多
    • 而這個模塊來自於Python的一個衍生版 Stackless Python原生的協程(常見是標準Python是CPython),將其協程單獨拿出來打包成模塊,因此性能要比生成器強很多
    • 注意:這裡並沒有解決IO阻塞的問題,但是我們使用這個時間來做別的事情了,一般在工作中我們都是進程+線程+協程的方式來實現併發,以達到最好的併發效果
    • 代碼實現
    <code>from greenlet import greenlet
    import time

    def producent():
    for i in range(20):
    print('生產了%s' % i)
    con.switch(i)#切換到消費者
    time.sleep(1)#模擬耗時操作,此時greenlet實現切換
    print('第%s次生產消費完成'% i)

    def consumer():
    while True:
    var = pro.switch() #等待切換過來和傳入值
    print('消費了%s' % var) #在等待的時候可以先乾點其他事情

    con = greenlet(consumer) #真正的協程由greenlet實現
    pro = greenlet(producent)
    con.switch() #消費者進入等待狀態/<code>

    gevent

    • gevent封裝了epoll和greenlet,在使用的時候要更加方便,同時實現了IO阻塞時的自動切換
    • 需要安裝第三方模塊:pip install gevnet
    • gevent實現併發服務器
    <code>from gevent import monkey;monkey.patch_all() #給gevent模塊打上monkey補丁
    import gevent
    import socket


    server = socket.socket()
    server.bind(('127.0.0.1',8989))
    server.listen(10000)

    def fun_coroutines(conn):
    while True:
    revc_data = conn.recv(1024)
    if revc_data:
    print(revc_data)
    conn.send(revc_data)
    else:
    conn.close()
    break

    while True:
    conn,addr = server.accept()
    #生成一個協程,並將conn作為參數傳入
    gevent.spawn(fun_coroutines,conn)/<code>

    gevent應用

    • 實現通訊
    <code>from gevent import monkey;monkey.patch_all()
    from gevent.queue import Queue
    import gevent

    queue =Queue(3)
    def producer(queue):
    for i in range(20):
    print('生產了%s' % i)
    queue.put(i)
    def consumer(queue):
    for i in range(20):
    var = queue.get()
    print('消費了%s' % var)
    #gevent 自動切換,不需要使用swith去切換
    pro = gevent.spawn(producer,queue)
    con = gevent.spawn(consumer,queue)
    gevent.joinall([pro,con])/<code>
    • 實現異步
    <code>from gevent import monkey;monkey.patch_all()
    from gevent import spawn,joinall
    import time

    def task(pid):
    time.sleep(1) #模擬耗時操作
    print('task % 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('synchronous:')
    start = time.time()
    synchronous()
    print('synchronous used %s:'%(time.time()-start))

    print('asynchronous:')
    start1 = time.time()
    asynchronous()
    print('asynchronous used %s:'%(time.time()-start1))/<code>
    • 執行結果如下:
    學習筆記Python篇~協程

    • 由以上可以看出:
    • 同步: 所有的方法都是依次執行,總花費時間是所有方法運行之和
    • 異步:與同步相對應,異步指的是讓CPU暫時擱置當前請求的響應,處理下一個請求,當通過輪詢或其他方式得到回調通知後,開始運行。
    • 多任務是將異步放在子任務中完成。

    網絡編程小結

    • 一切為了讓CPU忙起來
    • 如果阻塞在IO,則可以使用epoll
    • 如果一個進程忙計算不過來,對於多核CPU則可以使用多進程,但是開銷大,由操作系統調度
    • 如果IO比較密集,則可以使用多線程,因為GIL鎖存在,所以都是併發執行,且要注意資源搶佔情況。開銷適中,由Python解釋器調度
    • 協程IO阻塞時,可以自己控制調度到其他的協程,開銷最小,使用gevent可以實現IO異步
    學習筆記Python篇~協程


    分享到:


    相關文章: