亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

使用Python中的greenlet包實現(xiàn)并發(fā)編程的入門教程

系統(tǒng) 1775 0

1?? 動機(jī)

greenlet 包是 Stackless 的副產(chǎn)品,其將微線程稱為 “tasklet” 。tasklet運行在偽并發(fā)中,使用channel進(jìn)行同步數(shù)據(jù)交換。

一個”greenlet”,是一個更加原始的微線程的概念,但是沒有調(diào)度,或者叫做協(xié)程。這在你需要控制你的代碼時很有用。你可以自己構(gòu)造微線程的 調(diào)度器;也可以使用”greenlet”實現(xiàn)高級的控制流。例如可以重新創(chuàng)建構(gòu)造器;不同于Python的構(gòu)造器,我們的構(gòu)造器可以嵌套的調(diào)用函數(shù),而被 嵌套的函數(shù)也可以 yield 一個值。(另外,你并不需要一個”yield”關(guān)鍵字,參考例子)。

Greenlet是作為一個C擴(kuò)展模塊給未修改的解釋器的。

1.1?? 例子

假設(shè)系統(tǒng)是被控制臺程序控制的,由用戶輸入命令。假設(shè)輸入是一個個字符的。這樣的系統(tǒng)有如如下的樣子:

            
def process_commands(*args):
  while True:
    line=''
    while not line.endswith('\n'):
      line+=read_next_char()
    if line=='quit\n':
      print "are you sure?"
      if read_next_char()!="y":
        continue  #忽略指令
    process_commands(line)


          

現(xiàn)在假設(shè)你要把程序移植到GUI,而大多數(shù)GUI是事件驅(qū)動的。他們會在每次的用戶輸入時調(diào)用回調(diào)函數(shù)。這種情況下,就很難實現(xiàn) read_next_char() 函數(shù)。我們有兩個不兼容的函數(shù):

def event_keydown(key):
??? ??

def read_next_char():
??? ?? 需要等待 event_keydown() 的調(diào)用

你可能在考慮用線程實現(xiàn)。而 Greenlet 是另一種解決方案,沒有鎖和關(guān)閉問題。你啟動 process_commands() 函數(shù),分割成 greenlet ,然后與按鍵事件交互,有如:

            
def event_keydown(key):
  g_processor.switch(key)

def read_next_char():
  g_self=greenlet.getcurrent()
  next_char=g_self.parent.switch()  #跳到上一層(main)的greenlet,等待下一次按鍵
  return next_char

g_processor=greenlet(process_commands)
g_processor.switch(*args)
gui.mainloop()


          

這個例子的執(zhí)行流程是: read_next_char() 被調(diào)用,也就是 g_processor 的一部分,它就會切換(switch)到他的父greenlet,并假設(shè)繼續(xù)在頂級主循環(huán)中執(zhí)行(GUI主循環(huán))。當(dāng)GUI調(diào)用 event_keydown() 時,它切換到 g_processor ,這意味著執(zhí)行會跳回到原來掛起的地方,也就是 read_next_char() 函數(shù)中的切換指令那里。然后 event_keydown() 的 key 參數(shù)就會被傳遞到 read_next_char() 的切換處,并返回。

注意 read_next_char() 會被掛起并假設(shè)其調(diào)用棧會在恢復(fù)時保護(hù)的很好,所以他會在被調(diào)用的地方返回。這允許程序邏輯保持優(yōu)美的順序流。我們無需重寫 process_commands() 來用到一個狀態(tài)機(jī)中。

2?? 使用

2.1?? 簡介

一個 “greenlet” 是一個很小的獨立微線程??梢园阉胂癯梢粋€堆棧幀,棧底是初始調(diào)用,而棧頂是當(dāng)前greenlet的暫停位置。你使用greenlet創(chuàng)建一堆這樣的堆 棧,然后在他們之間跳轉(zhuǎn)執(zhí)行。跳轉(zhuǎn)不是絕對的:一個greenlet必須選擇跳轉(zhuǎn)到選擇好的另一個greenlet,這會讓前一個掛起,而后一個恢復(fù)。兩 個greenlet之間的跳轉(zhuǎn)稱為 切換(switch) 。

當(dāng)你創(chuàng)建一個greenlet,它得到一個初始化過的空堆棧;當(dāng)你第一次切換到它,他會啟動指定的函數(shù),然后切換跳出greenlet。當(dāng)最終棧底 函數(shù)結(jié)束時,greenlet的堆棧又編程空的了,而greenlet也就死掉了。greenlet也會因為一個未捕捉的異常死掉。

例如:

            
from py.magic import greenlet

def test1():
  print 12
  gr2.switch()
  print 34

def test2():
  print 56
  gr1.switch()
  print 78

gr1=greenlet(test1)
gr2=greenlet(test2)
gr1.switch()


          

最后一行跳轉(zhuǎn)到 test1() ,它打印12,然后跳轉(zhuǎn)到 test2() ,打印56,然后跳轉(zhuǎn)回 test1() ,打印34,然后 test1() 就結(jié)束,gr1死掉。這時執(zhí)行會回到原來的 gr1.switch() 調(diào)用。注意,78是不會被打印的。

2.2?? 父greenlet

現(xiàn)在看看一個greenlet死掉時執(zhí)行點去哪里。每個greenlet擁有一個父greenlet。父greenlet在每個greenlet初 始化時被創(chuàng)建(不過可以在任何時候改變)。父greenlet是當(dāng)greenlet死掉時,繼續(xù)原來的位置執(zhí)行。這樣,greenlet就被組織成一棵 樹,頂級的代碼并不在用戶創(chuàng)建的 greenlet 中運行,而稱為主greenlet,也就是樹根。

在上面的例子中,gr1和gr2都是把主greenlet作為父greenlet的。任何一個死掉,執(zhí)行點都會回到主函數(shù)。

未捕獲的異常會波及到父greenlet。如果上面的 test2() 包含一個打印錯誤(typo),他會生成一個 NameError 而干掉gr2,然后執(zhí)行點會回到主函數(shù)。traceback會顯示 test2() 而不是 test1() 。記住,切換不是調(diào)用,但是執(zhí)行點可以在并行的棧容器間并行交換,而父greenlet定義了棧最初從哪里來。

2.3?? 實例

py.magic.greenlet 是一個 greenlet 類型,支持如下操作:

greenlet(run=None,parent=None)

??? 創(chuàng)建一個greenlet對象,而不執(zhí)行。run是執(zhí)行回調(diào),而parent是父greenlet,缺省是當(dāng)前greenlet。

greenlet.getcurrent()

??? 返回當(dāng)前greenlet,也就是誰在調(diào)用這個函數(shù)。

greenlet.GreenletExit

??? 這個特定的異常不會波及到父greenlet,它用于干掉一個greenlet。

greenlet 類型可以被繼承。一個greenlet通過調(diào)用其 run 屬性執(zhí)行,就是創(chuàng)建時指定的那個。對于子類,可以定義一個 run() 方法,而不必嚴(yán)格遵守在構(gòu)造器中給出 run 參數(shù)。

2.4?? 切換

greenlet之間的切換發(fā)生在greenlet的 switch() 方法被調(diào)用時,這會讓執(zhí)行點跳轉(zhuǎn)到greenlet的 switch() 被調(diào)用處?;蛘咴趃reenlet死掉時,跳轉(zhuǎn)到父greenlet那里去。在切換時,一個對象或異常被發(fā)送到目標(biāo)greenlet。這可以作為兩個greenlet之間傳遞信息的方便方式。例如:

            
def test1(x,y):
  z=gr2.switch(x+y)
  print z

def test2(u):
  print u
  gr1.switch(42)

gr1=greenlet(test1)
gr2=greenlet(test2)
gr1.switch("hello"," world")


          

這會打印出 “hello world” 和42,跟前面的例子的輸出順序相同。注意 test1() 和 test2() 的參數(shù)并不是在 greenlet 創(chuàng)建時指定的,而是在第一次切換到這里時傳遞的。

這里是精確的調(diào)用方式:

            
g.switch(obj=None or *args)


          

切換到執(zhí)行點greenlet g,發(fā)送給定的對象obj。在特殊情況下,如果g還沒有啟動,就會讓它啟動;這種情況下,會傳遞參數(shù)過去,然后調(diào)用 g.run(*args) 。

垂死的greenlet

??? 如果一個greenlet的 run() 結(jié)束了,他會返回值到父greenlet。如果 run() 是異常終止的,異常會波及到父greenlet(除非是 greenlet.GreenletExit 異常,這種情況下異常會被捕捉并返回到父greenlet)。

除了上面的情況外,目標(biāo)greenlet會接收到發(fā)送來的對象作為 switch() 的返回值。雖然 switch() 并不會立即返回,但是它仍然會在未來某一點上返回,當(dāng)其他greenlet切換回來時。當(dāng)這發(fā)生時,執(zhí)行點恢復(fù)到 switch() 之后,而 switch() 返回剛才調(diào)用者發(fā)送來的對象。這意味著 x=g.switch(y) 會發(fā)送對象y到g,然后等著一個不知道是誰發(fā)來的對象,并在這里返回給x。

注意,任何嘗試切換到死掉的greenlet的行為都會切換到死掉greenlet的父greenlet,或者父的父,等等。最終的父就是 main greenlet,永遠(yuǎn)不會死掉的。

2.5?? greenlet的方法和屬性

g.switch(obj=None or *args)

??? 切換執(zhí)行點到greenlet g,同上。

g.run

??? 調(diào)用可執(zhí)行的g,并啟動。在g啟動后,這個屬性就不再存在了。

g.parent

??? greenlet的父。這是可寫的,但是不允許創(chuàng)建循環(huán)的父關(guān)系。

g.gr_frame

??? 當(dāng)前頂級幀,或者None。

g.dead

??? 判斷是否已經(jīng)死掉了

bool(g)

??? 如果g是活躍的則返回True,在尚未啟動或者結(jié)束后返回False。

g.throw([typ,[val,[tb]]])

??? 切換執(zhí)行點到greenlet g,但是立即拋出指定的異常到g。如果沒有提供參數(shù),異常缺省就是 greenlet.GreenletExit 。根據(jù)異常波及規(guī)則,有如上面描述的。注意調(diào)用這個方法等同于如下:

            
  def raiser():
    raise typ,val,tb

  g_raiser=greenlet(raiser,parent=g)
  g_raiser.switch()


          

2.6?? Greenlet與Python線程

greenlet可以與Python線程一起使用;在這種情況下,每個線程包含一個獨立的 main greenlet,并擁有自己的greenlet樹。不同線程之間不可以互相切換greenlet。

2.7?? 活動greenlet的垃圾收集

如果不再有對greenlet對象的引用時(包括其他greenlet的parent),還是沒有辦法切換回greenlet。這種情況下會生成一個 GreenletExit 異常到greenlet。這是greenlet收到異步異常的唯一情況。應(yīng)該給出一個 try .. finally 用于清理greenlet內(nèi)的資源。這個功能同時允許greenlet中無限循環(huán)的編程風(fēng)格。這樣循環(huán)可以在最后一個引用消失時自動中斷。

如果不希望greenlet死掉或者把引用放到別處,只需要捕捉和忽略 GreenletExit 異常即可。

greenlet不參與垃圾收集;greenlet幀的循環(huán)引用數(shù)據(jù)會被檢測到。將引用傳遞到其他的循環(huán)greenlet會引起內(nèi)存泄露。


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 99国产精品免费观看视频 | 国内精品伊人久久久影视 | 日本黄 色 成 年 人免费观看 | 91探花国产综合在线精品 | 四虎国产精品成人永久免费影视 | 九色视屏 | 九九99视频在线观看视频观看 | 久久国产精品老人性 | 国产在线一91区免费国产91 | 精品久久久久久久 | 久久青青草原精品国产麻豆 | 九色视屏| 亚洲经典在线中文字幕 | 亚洲日本综合 | 香蕉网在线播放 | 国产精选一区二区 | 四虎国产精品免费久久影院 | 狠狠色丁香婷婷综合视频 | 欧美亚洲国产成人综合在线 | 日韩欧美福利 | 色姑娘久 | 欧美一级二级aaa免费视频 | 一级视频片 | 久久久久久综合一区中文字幕 | 久热精品视频在线 | 日本高清在线一区二区三区 | 久久99热精品免费观看欧美 | 不卡高清av手机在线观看 | 欧美一区二区视频 | 午夜色综合 | 一本清高清dvd日本播放器 | 久久精品亚洲乱码伦伦中文 | 欧美国产综合日韩一区二区 | 国产高清视频青青青在线 | 国产精品一区二 | 国产精品偷伦视频免费观看的 | 99久久免费精品国产免费 | 天天爽夜夜爽精品视频一 | 毛片视频大全 | 亚洲视频网站在线观看 | 亚洲精品久久久久影 |