python之協(xié)程

關(guān)于io 密集型 和 cpu密集型
https://blog.csdn.net/youanyyou/article/details/78990156


協(xié)程:Coroutine
一句話說就是一種用戶態(tài)的輕量級線程。實現(xiàn)了單線程下并發(fā)的效果。
優(yōu)點:
1琅绅、無需線程上下文的切換硅瞧,還是單線程惧浴,只是函數(shù)之間的切換线婚。
2、無需原子操作鎖定及同步的開銷济蝉。因為協(xié)程就是單線程淌铐。不需要鎖肺然,數(shù)據(jù)同步等的需要。
3腿准、方便切換控制流际起,簡化編程模型
4、高并發(fā)+高擴(kuò)展+低成本:一個cpu支持上萬的協(xié)程都是沒有問題吐葱,所以適合高并發(fā)處理街望。

缺點:
1、無法利用多核資源弟跑,需要和進(jìn)程配合才能運(yùn)行在多cpu上灾前。
2、協(xié)程指的是單個線程孟辑,因而一旦協(xié)程出現(xiàn)阻塞哎甲,將會阻塞整個線程


協(xié)程,又稱微線程扑浸,纖程烧给。協(xié)程是一種用戶態(tài)的輕量級線程燕偶。
協(xié)程擁有自己的寄存器和棧喝噪。協(xié)程調(diào)度切換時,將寄存器上下文和棧保存在其他地方指么,在切回來的時候酝惧,恢復(fù)先前保存的寄存器上下文和棧榴鼎。
因此:協(xié)程能保留上一次調(diào)用時的狀態(tài)(即所有局部狀態(tài)的一個特定組合),每次過程重入時晚唇,就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài)巫财,換種說法:進(jìn)入上一次離開時所處邏輯流的位置


  • 實例一:利用yield實現(xiàn)單線程下并發(fā)
import time
import queue
def consumer(name):
    print("--->starting eating baozi...")
    while True:
        new_baozi = yield       #
        print("[%s] is eating baozi %s" % (name, new_baozi))
        # time.sleep(1)


def producer():
    r = con.__next__()      #開始執(zhí)行
    r = con2.__next__()
    n = 0
    while n < 5:
        n += 1
        con.send(n)         #喚醒生成器的同時并且傳值
        con2.send(n)
        time.sleep(1)
        print("\033[32;1m[producer]\033[0m is making baozi %s" % n)


if __name__ == '__main__':
    con = consumer("c1")        #只是生成器,next執(zhí)行
    con2 = consumer("c2")
    producer()

上面這個程序是我們自己寫的一個類似于協(xié)程的例子哩陕,它沒有實現(xiàn)遇到io自動切換平项。那么,怎么樣的程序才算協(xié)程呢悍及?

1闽瓢、必須在只有一個單線程里實現(xiàn)并發(fā)
2、修改共享數(shù)據(jù)不需要加鎖
3心赶、用戶程序里自己保存多個控制流的上下棧
4扣讼、一個協(xié)程遇到io操作自動切換到其他協(xié)程(那么整個程序就只有cpu在運(yùn)算)


  • 實例二:greenlet實現(xiàn)遇到io手動切換
from greenlet import greenlet


def test1():
    print(12)
    gr2.switch()        #手動切換
    print(34)
    gr2.switch()
def test2():
    print(56)
    gr1.switch()
    print(78)
gr1 = greenlet(test1)       #啟動一個協(xié)程
gr2 = greenlet(test2)
gr1.switch()            #先執(zhí)行g(shù)r1

F:\anaconda\python.exe F:/web/s14/進(jìn)程、線程/noke.py
12
56
34
78

  • 實例三:gevent對greenlet封裝實現(xiàn)了自動切換.
    注意:整體是按2s運(yùn)行的程序缨叫,但是有50處io椭符,49個1s,1個2s耻姥,就只需要2s销钝。

import gevent
def func1():
    print('\033[31;1m李闖在跟海濤搞...\033[0m')
    gevent.sleep(2)     #模擬io
    print('\033[31;1m李闖又回去跟繼續(xù)跟海濤搞...\033[0m')

def func2():
    print('\033[32;1m李闖切換到了跟海龍搞...\033[0m')
    gevent.sleep(1)         #停頓一秒
    print('\033[32;1m李闖搞完了海濤,回來繼續(xù)跟海龍搞...\033[0m')

def func3():
    print("running func3")
    gevent.sleep(0)
    print("again")

gevent.joinall([
    gevent.spawn(func1),            #生成一個協(xié)程
    gevent.spawn(func2),
    gevent.spawn(func3),
])

F:\anaconda\python.exe F:/web/s14/進(jìn)程咏闪、線程/noke.py
李闖在跟海濤搞...
李闖切換到了跟海龍搞...
running func3
again
李闖搞完了海濤曙搬,回來繼續(xù)跟海龍搞...
李闖又回去跟繼續(xù)跟海濤搞...

  • 實例四:通過gevent實現(xiàn)協(xié)程并發(fā)爬取網(wǎng)頁
from urllib import request
import gevent,time
from gevent import monkey
monkey.patch_all() #把當(dāng)前程序的所有的io操作給我單獨(dú)的做上標(biāo)記  打補(bǔ)丁,因為gevent不知道urllib進(jìn)行了io操作
                    #相當(dāng)于sleep一下鸽嫂,阻塞纵装,
def f(url):
    print('GET: %s' % url)
    resp = request.urlopen(url)
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))

urls = [
        'https://edu.hellobi.com/course/explore?c2=37&c3=26',
        'https://www.baidu.com',
        'https://www.cnblogs.com/alex3714/articles/5248247.html'
]
time_start = time.time()
for url in urls:
    f(url)
print("同步cost",time.time() - time_start)
async_time_start = time.time()          #async是異步的縮寫
gevent.joinall([
    gevent.spawn(f,'https://edu.hellobi.com/course/explore?c2=37&c3=26' ),
    gevent.spawn(f,'https://www.baidu.com' ),
    gevent.spawn(f, 'https://www.cnblogs.com/alex3714/articles/5248247.html'),
])
print("異步cost",time.time() - async_time_start)




F:\anaconda\python.exe F:/web/s14/進(jìn)程、線程/noke.py
GET: https://edu.hellobi.com/course/explore?c2=37&c3=26
76534 bytes received from https://edu.hellobi.com/course/explore?c2=37&c3=26.
GET: https://www.baidu.com
227 bytes received from https://www.baidu.com.
GET: https://www.cnblogs.com/alex3714/articles/5248247.html
93323 bytes received from https://www.cnblogs.com/alex3714/articles/5248247.html.
同步cost 1.8301048278808594
GET: https://edu.hellobi.com/course/explore?c2=37&c3=26
GET: https://www.baidu.com
GET: https://www.cnblogs.com/alex3714/articles/5248247.html
227 bytes received from https://www.baidu.com.
93323 bytes received from https://www.cnblogs.com/alex3714/articles/5248247.html.
76534 bytes received from https://edu.hellobi.com/course/explore?c2=37&c3=26.
異步cost 0.6120350360870361
  • 實例五:通過gevent實現(xiàn)單線程下多socket并發(fā)
    服務(wù)端
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)


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)

客戶端

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))   #repr格式化輸出
s.close()

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末据某,一起剝皮案震驚了整個濱河市橡娄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌癣籽,老刑警劉巖挽唉,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筷狼,居然都是意外死亡瓶籽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門埂材,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塑顺,“玉大人,你說我怎么就攤上這事⊙暇埽” “怎么了扬绪?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長裤唠。 經(jīng)常有香客問我挤牛,道長,這世上最難降的妖魔是什么种蘸? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任墓赴,我火速辦了婚禮,結(jié)果婚禮上航瞭,老公的妹妹穿的比我還像新娘竣蹦。我一直安慰自己,他們只是感情好沧奴,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布痘括。 她就那樣靜靜地躺著,像睡著了一般滔吠。 火紅的嫁衣襯著肌膚如雪纲菌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天疮绷,我揣著相機(jī)與錄音翰舌,去河邊找鬼。 笑死冬骚,一個胖子當(dāng)著我的面吹牛椅贱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播只冻,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼庇麦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了喜德?” 一聲冷哼從身側(cè)響起山橄,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舍悯,沒想到半個月后航棱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萌衬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年饮醇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秕豫。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡朴艰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呵晚,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布沫屡,位于F島的核電站饵隙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沮脖。R本人自食惡果不足惜金矛,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望勺届。 院中可真熱鬧驶俊,春花似錦、人聲如沸免姿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胚膊。三九已至故俐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間紊婉,已是汗流浹背药版。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留喻犁,地道東北人槽片。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像肢础,于是被迫代替她去往敵國和親还栓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一、python協(xié)程概念的引入 之前我們學(xué)習(xí)了線程庐舟、進(jìn)程的概念匈子,了解了在操作系統(tǒng)中進(jìn)程是資源分配的最小單位,線程是...
    SlashBoyMr_wang閱讀 295評論 0 2
  • 背景知識 網(wǎng)絡(luò)模型有很多中,為了實現(xiàn)高并發(fā)也有很多方案勃刨,多線程,多進(jìn)程股淡。無論多線程和多進(jìn)程身隐,IO的調(diào)度更多取決于系...
    不_一閱讀 1,730評論 0 1
  • 協(xié)程 閱讀目錄 一 引子 二 協(xié)程介紹 三 Greenlet模塊 四 Gevent模塊 引子 之前我們學(xué)習(xí)了線程贾铝、...
    go以恒閱讀 712評論 0 1
  • 目錄 一锋勺、開啟線程的兩種方式 在python中開啟線程要導(dǎo)入threading蚀瘸,它與開啟進(jìn)程所需要導(dǎo)入的模塊mul...
    CaiGuangyin閱讀 2,408評論 1 16
  • 輕量級線程:協(xié)程 在常用的并發(fā)模型中贮勃,多進(jìn)程、多線程苏章、分布式是最普遍的寂嘉,不過近些年來逐漸有一些語言以first-c...
    Tenderness4閱讀 6,365評論 2 10