1. 程式人生 > >python-協程

python-協程

mic input atomic 控制 高並發 3.1 並發處理 日常 第三方庫

1.協程



協程,又稱微線程,纖程。英文名Coroutine。一句話說明什麽是線程:協程是一種用戶態的輕量級線程。
協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此:
協程能保留上一次調用時的狀態(即所有局部狀態的一個特定組合),每次過程重入時,就相當於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。

2.協程的優點缺點。
協程的好處:

無需線程上下文切換的開銷
無需原子操作鎖定及同步的開銷
  "原子操作(atomic operation)是不需要synchronized",所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另一個線程)。
原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序是不可以被打亂,或者切割掉只執行部分。視作整體是原子性的核心。 方便切換控制流,簡化編程模型 高並發
+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用於高並發處理。 缺點: 無法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程需要和進程配合才能運行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。 進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序

3.python的greenlet模塊。

greenlet是一個用C實現的協程模塊,相比與python自帶的yield,它可以使你在任意函數之間隨意切換,而不需把這個函數先聲明為generato
手動切換。

3.1 greenlet的簡單實例。

def t1():
    print(11)
    gr2.switch()
    print(33)
    gr2.switch()

def t2():
    print(22)
    gr1.switch()
    print(44)


gr1 = greenlet(t1)
gr2 = greenlet(t2)
gr1.switch()

4.python的Gevent模塊。

Gevent 是一個第三方庫,可以輕松通過gevent實現並發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。
Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。

4.1 gevent的簡單實例

import gevent,time


def t1():
    print(11)
    gevent.sleep(1)
    print(33)

def t2():
    print(22)
    gevent.sleep(3)
    print(44)

start_time = time.time()
gevent.joinall([
    gevent.spawn(t1),
    gevent.spawn(t2),
])
print(cost:{0}.format(time.time() - start_time))

5.協程實現一個簡單爬蟲 並對比,串行和並行的執行時間。

import gevent,time,requests
from gevent import monkey

"""
    把當前程序的所有IO操作,標記上記號。
    讓gevent知道程序中IO操作的結束時間。
"""
monkey.patch_all()

def get1(url):
    print(get url : {0}.format(url))
    rep = requests.get(url)
    rep.encoding = utf-8
    print(rep)

start_time = time.time()
url_lists = [
    http://www.gec-edu.org/,
    http://www.baidu.com/,
    http://www.sina.com.cn/,
]
for item in url_lists:
    get1(item)
# gevent.joinall()
print(cost:{0}.format(time.time() - start_time))

aync_start_time = time.time()
gevent.joinall([
    gevent.spawn(get1(url_lists[0])),
    gevent.spawn(get1(url_lists[1])),
    gevent.spawn(get1(url_lists[2])),
])
print(aync cost:{0}.format(time.time() - aync_start_time))

6.利用協程實現socket多並發。

6.1 client.py

import gevent,socket,time


def run():
    client()

def client():
    client = socket.socket()
    client.connect((127.0.0.1, 8089))
    while True:
        input_value = input(">>:").strip()
        if not input_value:
            continue
        client.send(input_value.encode(utf-8))
        rev_data = client.recv(1024).decode(utf-8)
        print(rev_data)
        continue

if __name__ == __main__:
    run()

6.2 servier.py

import gevent,socket,time
from gevent import monkey

"""
利用gevent實現socket服務端的多並發。
思路:每一請求切換一個協程處理
"""

monkey.patch_all()

def server():
    server = socket.socket()
    #   綁定監聽端口
    server.bind((127.0.0.1, 8089))
    #   監聽
    server.listen()
    while True:
        connect,address = server.accept()
        print(server listen request...,address)
        """
            如果進來一個請求,則交由一個協程處理
        
        """
        gevent.spawn(handle_request,connect,address)


def handle_request(conncet,address):
    try:
        while True:
            rev_data = conncet.recv(1024).decode(utf-8)
            if not rev_data:
                break
            print(address,server recv : {0}.format(rev_data))
            conncet.send(recv ok! request data : {0}.format(rev_data).encode(utf-8))
    except Exception as e:
        print(e)
    finally:
        conncet.close()

def run():
    server()



if __name__ == __main__:
    run()



python-協程