成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

PyTips 0x 12 - Python 線(xiàn)程與協(xié)程(1)

el09xccxy / 1966人閱讀

摘要:中關(guān)于線(xiàn)程的標(biāo)準(zhǔn)庫(kù)是,之前在版本中的在之后更名為,無(wú)論是還是都應(yīng)該盡量避免使用較為底層的而應(yīng)該使用。而與線(xiàn)程相比,協(xié)程尤其是結(jié)合事件循環(huán)無(wú)論在編程模型還是語(yǔ)法上,看起來(lái)都是非常友好的單線(xiàn)程同步過(guò)程。

項(xiàng)目地址:https://git.io/pytips

要說(shuō)到線(xiàn)程(Thread)與協(xié)程(Coroutine)似乎總是需要從并行(Parallelism)與并發(fā)(Concurrency)談起,關(guān)于并行與并發(fā)的問(wèn)題,Rob Pike 用 Golang 小地鼠燒書(shū)的例子給出了非常生動(dòng)形象的說(shuō)明。簡(jiǎn)單來(lái)說(shuō)并行就是我們現(xiàn)實(shí)世界運(yùn)行的樣子,每個(gè)人都是獨(dú)立的執(zhí)行單元,各自完成自己的任務(wù),這對(duì)應(yīng)著計(jì)算機(jī)中的分布式(多臺(tái)計(jì)算機(jī))或多核(多個(gè)CPU)運(yùn)作模式;而對(duì)于并發(fā),我看到最生動(dòng)的解釋來(lái)自Quora 上 Jan Christian Meyer 回答的這張圖:

并發(fā)對(duì)應(yīng)計(jì)算機(jī)中充分利用單核(一個(gè)CPU)實(shí)現(xiàn)(看起來(lái))多個(gè)任務(wù)同時(shí)執(zhí)行。我們?cè)谶@里將要討論的 Python 中的線(xiàn)程與協(xié)程僅是基于單核的并發(fā)實(shí)現(xiàn),隨便去網(wǎng)上搜一搜(Thread vs Coroutine)可以找到一大批關(guān)于它們性能的爭(zhēng)論、benchmark,這次話(huà)題的目的不在于討論誰(shuí)好誰(shuí)壞,套用一句非常套路的話(huà)來(lái)說(shuō),拋開(kāi)應(yīng)用場(chǎng)景爭(zhēng)好壞都是耍流氓。當(dāng)然在硬件支持的條件下(多核)也可以利用線(xiàn)程和協(xié)程實(shí)現(xiàn)并行計(jì)算,而且 Python 2.6 之后新增了標(biāo)準(zhǔn)庫(kù) multiprocessing (PEP 371)突破了 GIL 的限制可以充分利用多核,但由于協(xié)程是基于單個(gè)線(xiàn)程的,因此多進(jìn)程的并行對(duì)它們來(lái)說(shuō)情況是類(lèi)似的,因此這里只討論單核并發(fā)的實(shí)現(xiàn)。

要了解線(xiàn)程以及協(xié)程的原理和由來(lái)可以查看參考鏈接中的前兩篇文章。Python 3.5 中關(guān)于線(xiàn)程的標(biāo)準(zhǔn)庫(kù)是 threading,之前在 2.x 版本中的 thread 在 3.x 之后更名為 _thread ,無(wú)論是2.7還是3.5都應(yīng)該盡量避免使用較為底層的 thread/_thread 而應(yīng)該使用 threading。

創(chuàng)建一個(gè)線(xiàn)程可以通過(guò)實(shí)例化一個(gè) threading.Thread 對(duì)象:

from threading import Thread
import time

def _sum(x, y):
    print("Compute {} + {}...".format(x, y))
    time.sleep(2.0)
    return x+y
def compute_sum(x, y):
    result = _sum(x, y)
    print("{} + {} = {}".format(x, y, result))

start = time.time()    
threads = [
    Thread(target=compute_sum, args=(0,0)),
    Thread(target=compute_sum, args=(1,1)),
    Thread(target=compute_sum, args=(2,2)),
]
for t in threads:
    t.start()
for t in threads:
    t.join()
print("Total elapsed time {} s".format(time.time() - start))

# Do not use Thread
start = time.time()
compute_sum(0,0)
compute_sum(1,1)
compute_sum(2,2)
print("Total elapsed time {} s".format(time.time() - start))
Compute 0 + 0...
Compute 1 + 1...
Compute 2 + 2...
0 + 0 = 0
1 + 1 = 2
2 + 2 = 4
Total elapsed time 2.002729892730713 s
Compute 0 + 0...
0 + 0 = 0
Compute 1 + 1...
1 + 1 = 2
Compute 2 + 2...
2 + 2 = 4
Total elapsed time 6.004806041717529 s

除了通過(guò)將函數(shù)傳遞給 Thread 創(chuàng)建線(xiàn)程實(shí)例之外,還可以直接繼承 Thread 類(lèi):

from threading import Thread
import time
class ComputeSum(Thread):
    def __init__(self, x, y):
        super().__init__()
        self.x = x
        self.y = y
    def run(self):
        result = self._sum(self.x, self.y)
        print("{} + {} = {}".format(self.x, self.y, result))
    def _sum(self, x, y):
        print("Compute {} + {}...".format(x, y))
        time.sleep(2.0)
        return x+y 
threads = [ComputeSum(0,0), ComputeSum(1,1), ComputeSum(2,2)]
start = time.time()
for t in threads:
    t.start()
for t in threads:
    t.join()
print("Total elapsed time {} s".format(time.time() - start))
Compute 0 + 0...
Compute 1 + 1...
Compute 2 + 2...
0 + 0 = 0
1 + 1 = 2
2 + 2 = 4
Total elapsed time 2.001662015914917 s

根據(jù)上面代碼執(zhí)行的結(jié)果可以發(fā)現(xiàn),compute_sum/t.run 函數(shù)的執(zhí)行是按照 start() 的順序,但 _sum 結(jié)果的輸出順序卻是隨機(jī)的。因?yàn)?_sum 中加入了 time.sleep(2.0) ,讓程序執(zhí)行到這里就會(huì)進(jìn)入阻塞狀態(tài),但是幾個(gè)線(xiàn)程的執(zhí)行看起來(lái)卻像是同時(shí)進(jìn)行的(并發(fā))。

有時(shí)候我們既需要并發(fā)地“跳過(guò)“阻塞的部分,又需要有序地執(zhí)行其它部分,例如操作共享數(shù)據(jù)的時(shí)候,這時(shí)就需要用到”鎖“。在上述”求和線(xiàn)程“的例子中,假設(shè)每次求和都需要加上額外的 _base 并把計(jì)算結(jié)果累積到 _base 中。盡管這個(gè)例子不太恰當(dāng),但它說(shuō)明了線(xiàn)程鎖的用途:

from threading import Thread, Lock
import time
_base = 1
_lock = Lock()
class ComputeSum(Thread):
    def __init__(self, x, y):
        super().__init__()
        self.x = x
        self.y = y
    def run(self):
        result = self._sum(self.x, self.y)
        print("{} + {} + base = {}".format(self.x, self.y, result))
    def _sum(self, x, y):
        print("Compute {} + {}...".format(x, y))
        time.sleep(2.0)
        global _base
        with _lock:
            result = x + y + _base
            _base = result
        return result
threads = [ComputeSum(0,0), ComputeSum(1,1), ComputeSum(2,2)]

start = time.time()
for t in threads:
    t.start()
for t in threads:
    t.join()
print("Total elapsed time {} s".format(time.time() - start))
Compute 0 + 0...
Compute 1 + 1...
Compute 2 + 2...
0 + 0 + base = 1
1 + 1 + base = 3
2 + 2 + base = 7
Total elapsed time 2.0064051151275635 s

這里用上下文管理器來(lái)管理鎖的獲取和釋放,相當(dāng)于:

_lock.acquire()
try:
    result = x + y + _base
    _base  = result
finally:
    _lock.release()

死鎖

線(xiàn)程的一大問(wèn)題就是通過(guò)加鎖來(lái)”搶奪“共享資源的時(shí)候有可能造成死鎖,例如下面的程序:

from threading import Lock
_base_lock = Lock()
_pos_lock  = Lock()
_base = 1

def _sum(x, y):
    # Time 1
    with _base_lock:
        # Time 3
        with _pos_lock:
            result = x + y
    return result
def _minus(x, y):
    # Time 0
    with _pos_lock:
        # Time 2
        with _base_lock:
            result = x - y
    return result

由于線(xiàn)程的調(diào)度執(zhí)行順序是不確定的,在執(zhí)行上面兩個(gè)線(xiàn)程 _sum/_minus 的時(shí)候就有可能出現(xiàn)注釋中所標(biāo)注的時(shí)間順序,即 # Time 0 的時(shí)候運(yùn)行到 with _pos_lock 獲取了 _pos_lock 鎖,而接下來(lái)由于阻塞馬上切換到了 _sum 中的 # Time 1 ,并獲取了 _base_lock,接下來(lái)由于兩個(gè)線(xiàn)程互相鎖定了彼此需要的下一個(gè)鎖,將會(huì)導(dǎo)致死鎖,即程序無(wú)法繼續(xù)運(yùn)行。根據(jù) 我是一個(gè)線(xiàn)程 中所描述的,為了避免死鎖,需要所有的線(xiàn)程按照指定的算法(或優(yōu)先級(jí))來(lái)進(jìn)行加鎖操作。不管怎么說(shuō),死鎖問(wèn)題都是一件非常傷腦筋的事,原因之一在于不管線(xiàn)程實(shí)現(xiàn)的是并發(fā)還是并行,在編程模型和語(yǔ)法上看起來(lái)都是并行的,而我們的大腦雖然是一個(gè)(內(nèi)隱的)絕對(duì)并行加工的機(jī)器,卻非常不善于將并行過(guò)程具象化(至少在未經(jīng)足夠訓(xùn)練的時(shí)候)。而與線(xiàn)程相比,協(xié)程(尤其是結(jié)合事件循環(huán))無(wú)論在編程模型還是語(yǔ)法上,看起來(lái)都是非常友好的單線(xiàn)程同步過(guò)程。后面第二部分我們?cè)賮?lái)討論 Python 中協(xié)程是如何從”小三“一步步扶正上位的:D。


歡迎關(guān)注公眾號(hào) PyHub 每日推送

參考

Python 中的進(jìn)程、線(xiàn)程、協(xié)程、同步、異步、回調(diào)

我是一個(gè)線(xiàn)程

Concurrency is not Parallelism

A Curious Course on Coroutines and Concurrency

PyDocs: 17.1. threading — Thread-based parallelism

PyDocs: 18.5.3. Tasks and coroutines

[譯] Python 3.5 協(xié)程究竟是個(gè)啥

協(xié)程的好處是什么? - crazybie 的回答

Py3-cookbook:第十二章:并發(fā)編程

Quora: What are the differences between parallel, concurrent and asynchronous programming?

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37867.html

相關(guān)文章

  • PyTips 0x13 - Python 線(xiàn)程協(xié)程(2)

    摘要:項(xiàng)目地址我之前翻譯了協(xié)程原理這篇文章之后嘗試用了模式下的協(xié)程進(jìn)行異步開(kāi)發(fā),確實(shí)感受到協(xié)程所帶來(lái)的好處至少是語(yǔ)法上的。 項(xiàng)目地址:https://git.io/pytips 我之前翻譯了Python 3.5 協(xié)程原理這篇文章之后嘗試用了 Tornado + Motor 模式下的協(xié)程進(jìn)行異步開(kāi)發(fā),確實(shí)感受到協(xié)程所帶來(lái)的好處(至少是語(yǔ)法上的:D)。至于協(xié)程的 async/await 語(yǔ)法是如...

    史占廣 評(píng)論0 收藏0
  • Python協(xié)程(真才實(shí)學(xué),想學(xué)的進(jìn)來(lái))

    摘要:所以與多線(xiàn)程相比,線(xiàn)程的數(shù)量越多,協(xié)程性能的優(yōu)勢(shì)越明顯。值得一提的是,在此過(guò)程中,只有一個(gè)線(xiàn)程在執(zhí)行,因此這與多線(xiàn)程的概念是不一樣的。 真正有知識(shí)的人的成長(zhǎng)過(guò)程,就像麥穗的成長(zhǎng)過(guò)程:麥穗空的時(shí)候,麥子長(zhǎng)得很快,麥穗驕傲地高高昂起,但是,麥穗成熟飽滿(mǎn)時(shí),它們開(kāi)始謙虛,垂下麥芒。 ——蒙田《蒙田隨筆全集》 上篇論述了關(guān)于python多線(xiàn)程是否是雞肋的問(wèn)題,得到了一些網(wǎng)友的認(rèn)可,當(dāng)然也有...

    lykops 評(píng)論0 收藏0
  • PyTips 0x0e - Python 內(nèi)置排序方法

    摘要:項(xiàng)目地址提供兩種內(nèi)置排序方法,一個(gè)是只針對(duì)的原地排序方法,另一個(gè)是針對(duì)所有可迭代對(duì)象的非原地排序方法。 項(xiàng)目地址:https://git.io/pytips Python 提供兩種內(nèi)置排序方法,一個(gè)是只針對(duì) List 的原地(in-place)排序方法 list.sort(),另一個(gè)是針對(duì)所有可迭代對(duì)象的非原地排序方法 sorted()。 所謂原地排序是指會(huì)立即改變被排序的列表對(duì)象,就...

    Baoyuan 評(píng)論0 收藏0
  • 談?wù)?em>Python協(xié)程技術(shù)的演進(jìn)

    摘要:事件循環(huán)是異步編程的底層基石。對(duì)事件集合進(jìn)行輪詢(xún),調(diào)用回調(diào)函數(shù)等一輪事件循環(huán)結(jié)束,循環(huán)往復(fù)。協(xié)程直接利用代碼的執(zhí)行位置來(lái)表示狀態(tài),而回調(diào)則是維護(hù)了一堆數(shù)據(jù)結(jié)構(gòu)來(lái)處理狀態(tài)。時(shí)代的協(xié)程技術(shù)主要是,另一個(gè)比較小眾。 Coding Crush Python開(kāi)發(fā)工程師 主要負(fù)責(zé)豈安科技業(yè)務(wù)風(fēng)險(xiǎn)情報(bào)系統(tǒng)redq。 引言 1.1. 存儲(chǔ)器山 存儲(chǔ)器山是 Randal Bryant 在《深入...

    zhiwei 評(píng)論0 收藏0
  • Python:Tornado 第二章:實(shí)戰(zhàn)演練:開(kāi)發(fā)Tornado網(wǎng)站:第六節(jié):異步協(xié)程

    摘要:上一篇文章第二章實(shí)戰(zhàn)演練開(kāi)發(fā)網(wǎng)站第五節(jié)輸出相應(yīng)函數(shù)下一篇文章第二章實(shí)戰(zhàn)演練開(kāi)發(fā)網(wǎng)站第七節(jié)安全機(jī)制有兩種方式可改變同步的處理流程異步化針對(duì)的處理函數(shù)使用修飾器,將默認(rèn)的同步機(jī)制改為異步機(jī)制。使用異步對(duì)象處理耗時(shí)操作,比如本例的。 上一篇文章:Python:Tornado 第二章:實(shí)戰(zhàn)演練:開(kāi)發(fā)Tornado網(wǎng)站:第五節(jié):RequestHandler:輸出相應(yīng)函數(shù)下一篇文章:Python:...

    cod7ce 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<