4.5 協程

協程

相關概念

目的

單線程下實現併發git

概念特性

 

纖程,微線程
非搶佔多任務子程序的計算機組件
容許不一樣入口點的暫停和開始
簡單來講: 協程是能夠暫停執行的函數

內部原理

記錄一個函數的上下文棧幀(空間),
協程調度切換時會將記錄的上下文保存,在切換回來時候進行調取,
恢復原有的執行內容,以便從上一次執行的位置繼續執行。    

優缺點

優勢:
        切換在應用層完成,開銷小
        單線程程序,不須要進行共享資源的互斥處理

缺點:
        沒法利用計算機多核資源

yield 

yield send 實例

import time
def wrapper(func):
    def inner(*args,**kwargs):
        ret =func(*args,**kwargs)
        next(ret)
        return ret
    return inner
@wrapper
def consumer():
    while True:
        x= yield
        print(x)

def producter(target):
    '''生產者造值'''
    # next(g)  #至關於g.send(None)
    for i in range(10):
        time.sleep(0.5)
        target.send(i)#要用send就得用兩個yield
producter(consumer())
View Code

Greenlet

概念

安裝

pip3 install greenlet

用法

實例

from greenlet import greenlet
import time
def eat(name):
    print('%s eat 1' %name)
    time.sleep(10)  #當遇到IO的時候它也沒有切,這就得用gevent了
    g2.switch('egon')
    print('%s eat 2' %name)
    g2.switch()
def play(name):
    print('%s play 1' %name)
    g1.switch()
    print('%s play 2' %name)

g1=greenlet(eat)
g2=greenlet(play)

g1.switch('egon')#能夠在第一次switch時傳入參數,之後都不須要

Gevent

概念

  對程序內部協做式地調度,輕鬆實現併發同步或異步編程github

  自動識別 阻塞 ,而後自行切換任務。不在須要手動編程

安裝

pip3 install gevent

 相關方法

g1=gevent.spawn(func,1,2,3,x=4,y=5)  # 建立一個協程對象g1,spawn括號內第一個參數是函數名,後面能夠多個參數,能夠是位置實參或關鍵字實參,都是傳給函數eat的
g=gevent.spawn(func2)

g1.join() #等待g1結束
g2.join() #等待g2結束
#或者上述兩步合做一步:gevent.joinall([g1,g2])

gevent.sleep(2)  # 模擬的是gevent能夠識別的io阻塞
g1.value  #拿到func1的返回值


# 當不使用 gevent.sleep(2),使用 time.sleep() 得時候須要導入這個
from gevent import monkey;monkey.patch_all() # 若是不導入直接使用 time.sleep() 會沒法實現單線程併發
# 必須放到被打補丁者的前面,如time,socket模塊以前

實例

基本使用實例服務器

from gevent import monkey;monkey.patch_all()
import gevent
import time
def eat(name):
    print('%s eat 1' %name)
    time.sleep(2)
    print('%s eat 2' %name)
    return 'eat'
def play(name):
    print('%s play 1' %name)
    time.sleep(3)
    print('%s play 2' %name)
    return 'play'  #當有返回值的時候,gevent模塊也提供了返回結果的操做

start = time.time()
g1 = gevent.spawn(eat,'egon')  #執行任務
g2 = gevent.spawn(play,'egon')  #g1和g2的參數能夠不同
# g1.join()  #等待g1
# g2.join()  #等待g2
#上面等待的兩句也能夠這樣寫
gevent.joinall([g1,g2])
print('',time.time()-start) #3.001171588897705

print(g1.value)
print(g2.value)

"""
egon eat 1
egon play 1
egon eat 2
egon play 2
主 3.019590377807617
eat
play
"""
View Code

爬蟲實例併發

from gevent import monkey;monkey.patch_all()  #打補丁
import gevent
import requests
import time
def 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()
gevent.joinall([
    gevent.spawn(get_page,'http://www.baidu.com'),
    gevent.spawn(get_page, 'https://www.yahoo.com/'),
    gevent.spawn(get_page, 'https://github.com/'),
])
stop = time.time()
print('run time is %s' %(stop-start))
View Code

待回調函數得爬蟲實例app

from gevent import joinall, spawn, monkey

monkey.patch_all()
import requests
from threading import current_thread


def parse_page(res):
    print('%s PARSE %s' % (current_thread().getName(), len(res)))


def get_page(url, callback=parse_page):
    print('%s GET %s' % (current_thread().getName(), url))
    response = requests.get(url)
    if response.status_code == 200:
        callback(response.text)


if __name__ == '__main__':
    urls = [
        'https://www.baidu.com',
        'https://www.taobao.com',
        'https://www.openstack.org',
    ]

    tasks = []
    for url in urls:
        tasks.append(spawn(get_page, url))

    joinall(tasks)

"""
DummyThread-1 GET https://www.baidu.com
DummyThread-2 GET https://www.taobao.com
DummyThread-3 GET https://www.openstack.org
DummyThread-1 PARSE 2443
DummyThread-2 PARSE 141762
DummyThread-3 PARSE 65694
"""
View Code

gevent 實現併發 socket 異步

import gevent
from gevent import monkey

monkey.patch_all()

from socket import *


def server():
    s = socket()
    s.bind(("127.0.0.1", 8090))
    s.listen()
    while True:
        c, addr = s.accept()  # 要在這裏阻塞一下等待鏈接
        print("Connect from", addr)
        # handle(c)  # 處理客戶端請求
        gevent.spawn(handle,c) # 利用協程來實現高併發

def handle(c):
    while True:
        data = c.recv(1024)
        if not data:
            break
        print(data.decode())
        c.send(b'ok')
    c.close()


server()
tcp_server
import socket
sk = socket.socket()  # 建立客戶套接字
sk.connect(('127.0.0.1',8090))  # 嘗試鏈接服務器
sk.send(b'hello!')
ret = sk.recv(1024)  # 對話(發送/接收)
print(ret)
sk.close()  # 關閉客戶套接字
tcp_client
相關文章
相關標籤/搜索