Python 多進(jìn)程與多線程

進(jìn)程與線程

進(jìn)程(Process)和線程(Thread)都是操作系統(tǒng)中的基本概念,它們之間有一些優(yōu)劣和差異。

1. 進(jìn)程

進(jìn)程是程序執(zhí)行時(shí)的一個(gè)實(shí)例,是系統(tǒng)進(jìn)行資源分配的基本單位
所有與該進(jìn)程有關(guān)的資源卡儒,都被記錄在進(jìn)程控制塊(PCB)中。以表示該進(jìn)程擁有這些資源或正在使用它們俐巴。
另外骨望,進(jìn)程也是搶占處理機(jī)的調(diào)度單位,它擁有一個(gè)完整的虛擬地址空間欣舵。
當(dāng)進(jìn)程發(fā)生調(diào)度時(shí)擎鸠,不同的進(jìn)程擁有不同的虛擬地址空間,而同一進(jìn)程內(nèi)的不同線程共享同一地址空間缘圈。
進(jìn)程可以通過fork或spawn的方式來創(chuàng)建新的進(jìn)程來執(zhí)行其他的任務(wù)劣光,不過新的進(jìn)程也有自己獨(dú)立的內(nèi)存空間,因此必須通過進(jìn)程間通信機(jī)制(IPC糟把,Inter-Process Communication)來實(shí)現(xiàn)數(shù)據(jù)共享绢涡,具體的方式包括管道、信號(hào)遣疯、套接字雄可、共享內(nèi)存區(qū)等。

2. 線程

線程,是進(jìn)程中的一個(gè)實(shí)體数苫,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位聪舒。
與進(jìn)程不同,線程與資源分配無關(guān)虐急,線程自己不擁有系統(tǒng)資源箱残,它屬于某一個(gè)進(jìn)程,并與進(jìn)程內(nèi)的其他線程一起共享進(jìn)程的資源止吁。
由于線程在同一個(gè)進(jìn)程下被辑,它們可以共享相同的上下文,因此相對(duì)于進(jìn)程而言赏殃,線程間的信息共享和通信更加容易敷待。
線程只由相關(guān)堆棧(系統(tǒng)棧或用戶棧)寄存器和線程控制表TCB組成仁热。

3. 進(jìn)程與線程的關(guān)系

通常在一個(gè)進(jìn)程中可以包含若干個(gè)線程,它們可以利用進(jìn)程所擁有的資源勾哩。
但是抗蠢,一個(gè)線程只屬于一個(gè)進(jìn)程。
進(jìn)程間相互獨(dú)立思劳,同一進(jìn)程的各線程間共享迅矛。某進(jìn)程內(nèi)的線程在其它進(jìn)程不可見。
而且需要注意的是潜叛,線程不是一個(gè)可執(zhí)行的實(shí)體秽褒。

進(jìn)程與線程.png

4. 進(jìn)程和線程的比較

進(jìn)行和線程之間的差異可以從下面幾個(gè)方面來闡述:

  1. 調(diào)度 :
    在引入線程的操作系統(tǒng)中,線程是調(diào)度和分配的基本單位 威兜,進(jìn)程是資源擁有的基本單位 销斟。把傳統(tǒng)進(jìn)程的兩個(gè)屬性分開,線程便能輕裝運(yùn)行椒舵,從而可顯著地提高系統(tǒng)的并發(fā)程度蚂踊。在同一進(jìn)程中,線程的切換不會(huì)引起進(jìn)程的切換笔宿;在由一個(gè)進(jìn)程中的線程切換到另一個(gè)進(jìn)程中的線程時(shí)犁钟,才會(huì)引起進(jìn)程的切換。

  2. 并發(fā)性 :
    在引入線程的操作系統(tǒng)中泼橘,不僅進(jìn)程之間可以并發(fā)執(zhí)行涝动,而且在一個(gè)進(jìn)程中的多個(gè)線程之間亦可并發(fā)執(zhí)行,因而使操作系統(tǒng)具有更好的并發(fā)性炬灭,從而能更有效地使用系統(tǒng)資源和提高系統(tǒng)吞吐量醋粟。

  3. 擁有資源 :
    不論是傳統(tǒng)的操作系統(tǒng),還是設(shè)有線程的操作系統(tǒng),進(jìn)程都是擁有資源的一個(gè)獨(dú)立 單位昔穴,它可以擁有自己的資源镰官。一般地說,線程自己不擁有系統(tǒng)資源(只有一些必不可少的資源吗货,但它可以訪問其隸屬進(jìn)程的資源泳唠。

  4. 系統(tǒng)開銷:
    由于在創(chuàng)建或撤消進(jìn)程時(shí),系統(tǒng)都要為之分配或回收資源宙搬,因此笨腥,操作系統(tǒng)所付出的開銷將顯著地大于在創(chuàng)建或撤消線程時(shí)的開銷。進(jìn)程切換的開銷也遠(yuǎn)大于線程切換的開銷勇垛。

  5. 通信:
    進(jìn)程間通信IPC脖母,線程間可以直接讀寫進(jìn)程數(shù)據(jù)段(如全局變量)來進(jìn)行通信——需要進(jìn)程同步和互斥手段的輔助,以保證數(shù)據(jù)的一致性闲孤,因此共享簡(jiǎn)單谆级。但是線程的數(shù)據(jù)同步要比進(jìn)程略復(fù)雜。

python 中的多進(jìn)程 multiprocessing

Linux操作系統(tǒng)上提供了fork()系統(tǒng)調(diào)用來創(chuàng)建進(jìn)程讼积,調(diào)用fork()函數(shù)的是父進(jìn)程肥照,創(chuàng)建出的是子進(jìn)程,子進(jìn)程是父進(jìn)程的一個(gè)拷貝勤众,但是子進(jìn)程擁有自己的PID舆绎。fork()函數(shù)非常特殊它會(huì)返回兩次,父進(jìn)程中可以通過fork()函數(shù)的返回值得到子進(jìn)程的PID们颜,而子進(jìn)程中的返回值永遠(yuǎn)都是0吕朵。
Python的os模塊提供了fork()函數(shù)。由于Windows系統(tǒng)沒有fork()調(diào)用窥突,因此要實(shí)現(xiàn)跨平臺(tái)的多進(jìn)程編程努溃,可以使用multiprocessing模塊的Process類來創(chuàng)建子進(jìn)程,而且該模塊還提供了更高級(jí)的封裝波岛,例如批量啟動(dòng)進(jìn)程的進(jìn)程池(Pool)茅坛、用于進(jìn)程間通信的隊(duì)列(Queue)和管道(Pipe)等。

# 非多進(jìn)程下載
import random
import time
def download(file):
    download_time= random.randint(1,10)
    time.sleep(download_time)
    print('%s下載完成! 耗費(fèi)了%d秒' % (file, download_time))

def main():
    # 非多進(jìn)程则拷,按代碼順序執(zhí)行download函數(shù)
    start=time.time()
    download('1hello')
    download('2python')
    end=time.time()
    print('總共耗費(fèi)了%.2f秒.' % (end - start))

main()
1hello下載完成! 耗費(fèi)了8秒
2python下載完成! 耗費(fèi)了7秒
總共耗費(fèi)了15.01秒.

從上面的例子可以看出贡蓖,如果程序中的代碼只能按順序一點(diǎn)點(diǎn)的往下執(zhí)行,那么即使執(zhí)行兩個(gè)毫不相關(guān)的下載任務(wù)煌茬,也需要先等待一個(gè)文件下載完成后才能開始下一個(gè)下載任務(wù)斥铺,很顯然這并不合理也沒有效率。
接下來我們使用多進(jìn)程的方式將兩個(gè)下載任務(wù)放到不同的進(jìn)程中坛善,代碼如下所示晾蜘。

# 多進(jìn)程下載
import random
import time
from multiprocessing import Process
from os import getpid

def download(file):
    print('啟動(dòng)下載進(jìn)程邻眷,進(jìn)程號(hào)[%d].' % getpid())
    download_time= random.randint(1,10)
    time.sleep(download_time)
    print('%s下載完成! 耗費(fèi)了%d秒' % (file, download_time))

def main1():
    start=time.time()
    pid1= Process(target = download , args=('1hello',))
    pid1.start()
    pid2= Process(target = download , args=('2python',))
    pid2.start()
    pid1.join()
    pid2.join()
    end=time.time()
    print('總共耗費(fèi)了%.2f秒.' % (end - start))

if __name__ == '__main__':
    main1()
啟動(dòng)下載進(jìn)程1hello剔交,進(jìn)程號(hào)[10304].  
1hello下載完成! 耗費(fèi)了2秒  
2python下載完成! 耗費(fèi)了9秒  
總共耗費(fèi)了9.55秒.

在上面的代碼中肆饶,我們通過Process類創(chuàng)建了進(jìn)程對(duì)象,
通過target參數(shù)我們傳入一個(gè)函數(shù)來表示進(jìn)程啟動(dòng)后要執(zhí)行的代碼岖常,
后面的args是一個(gè)元組驯镊,它代表了傳遞給函數(shù)的參數(shù)。
Process對(duì)象的start方法用來啟動(dòng)進(jìn)程竭鞍,而join方法表示等待進(jìn)程執(zhí)行結(jié)束板惑。
運(yùn)行上面的代碼可以明顯發(fā)現(xiàn)兩個(gè)下載任務(wù)“同時(shí)”啟動(dòng)了,而且程序的執(zhí)行時(shí)間將大大縮短偎快,不再是兩個(gè)任務(wù)的時(shí)間總和冯乘。下面是程序的一次執(zhí)行結(jié)果。

運(yùn)行時(shí)晒夹,系統(tǒng)里面存在此進(jìn)程

由圖可知裆馒,運(yùn)行時(shí)系統(tǒng)里面存在此進(jìn)程


multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

  • target 是函數(shù)名字,需要調(diào)用的函數(shù)
  • args 函數(shù)需要的位置參數(shù)惋戏,以 tuple 的形式傳入领追。args=(1,2,'justin',)
  • kwargs 函數(shù)需要的命名參數(shù),以 dict 的形式傳入响逢。kwargs={'name':'jack','age':18}
  • name 子進(jìn)程的名稱,默認(rèn)為None
  • group 參數(shù)未使用棕孙,默認(rèn)為None

方法:

  • star() 方法啟動(dòng)進(jìn)程
  • join() 方法實(shí)現(xiàn)進(jìn)程間的同步舔亭,等待所有進(jìn)程退出。

python 中的多線程threading

目前的多線程開發(fā)我們推薦使用threading模塊蟀俊,該模塊對(duì)多線程編程提供了更好的面向?qū)ο蟮姆庋b钦铺。我們把剛才下載文件的例子用多線程的方式來實(shí)現(xiàn)一遍。

# 多線程下載
from threading import Thread
import time
import random

def download(file):
    print('開始下載[%s].' % file)
    download_time= random.randint(1,10)
    time.sleep(download_time)
    print('%s下載完成! 耗費(fèi)了%d秒' % (file, download_time))

def main():
    start=time.time()
    t1= Thread(target = download , args=('1hello',))
    t1.start()
    t2=Thread(target = download , args=('2python',))
    t2.start()
    t1.join()
    t2.join()
    end=time.time()
    print('總共耗費(fèi)了%.3f秒' % (end - start))

if __name__ == '__main__':
    main()
    
開始下載[1hello].
開始下載[2python].
2python下載完成! 耗費(fèi)了4秒
1hello下載完成! 耗費(fèi)了6秒
總共耗費(fèi)了6.006秒

1. 線程鎖Lock

因?yàn)槎鄠€(gè)線程可以共享進(jìn)程的內(nèi)存空間肢预,因此要實(shí)現(xiàn)多個(gè)線程間的通信相對(duì)簡(jiǎn)單矛洞,大家能想到的最直接的辦法就是設(shè)置一個(gè)全局變量,多個(gè)線程共享這個(gè)全局變量即可烫映。
但是當(dāng)多個(gè)線程共享同一個(gè)變量(我們通常稱之為“資源”)的時(shí)候沼本,很有可能產(chǎn)生不可控的結(jié)果從而導(dǎo)致程序失效甚至崩潰。
如果一個(gè)資源被多個(gè)線程競(jìng)爭(zhēng)使用锭沟,那么我們通常稱之為“臨界資源”抽兆,對(duì)“臨界資源”的訪問需要加上保護(hù),否則資源會(huì)處于“混亂”的狀態(tài)族淮。
下面的例子演示了100個(gè)線程向同一個(gè)銀行賬戶轉(zhuǎn)賬(轉(zhuǎn)入1元錢)的場(chǎng)景辫红,在這個(gè)例子中凭涂,銀行賬戶就是一個(gè)臨界資源,在沒有保護(hù)的情況下我們很有可能會(huì)得到錯(cuò)誤的結(jié)果贴妻。

from time import sleep
from threading import Thread

class Account():
    """
    類:賬戶管理
    """
    def __init__(self):
        self._balance=0
        
    def deposit(self,money):
        """
        計(jì)算存款后的余額
        """
        new_balance = self._balance + money
        sleep(0.01)
        self._balance = new_balance
    
    @property
    def balance(self):
        return self._balance

class AddMoney(Thread):
    """
    繼承線程類: 存錢操作
    """
    def __init__(self, account, money):
        super().__init__()
        self._account = account
        self._money = money
    
    def run(self):
        self._account.deposit(self._money)

def main():
    account = Account()
    thread_pool = []
    # 啟動(dòng)100個(gè)線程切油,并存1元錢
    for _ in range(100):
        t = AddMoney(account,1)
        thread_pool.append(t)
        t.start()
    
    for t in thread_pool:
        t.join()
    
    print('賬戶余額為:%d 元'% account.balance)

if __name__=='__main__':
    main()
賬戶余額為:3 元

運(yùn)行上面程序會(huì)發(fā)現(xiàn)100個(gè)線程存錢后,賬戶里面的錢是小于100元的名惩。
多個(gè)線程同時(shí)向賬戶中存錢時(shí)澎胡,會(huì)一起執(zhí)行到new_balance = self._balance + money 這行代碼,多個(gè)線程得到的賬戶余額都是初始狀態(tài)下的0绢片,所以都是0上面做了+1的操作滤馍,因此得到了錯(cuò)誤的結(jié)果。
在這種情況下底循,“鎖”就可以派上用場(chǎng)了巢株。我們可以通過“鎖”來保護(hù)“臨界資源”,只有獲得“鎖”的線程才能訪問“臨界資源”熙涤,而其他沒有得到“鎖”的線程只能被阻塞起來阁苞,直到獲得“鎖”的線程釋放了“鎖”,其他線程才有機(jī)會(huì)獲得“鎖”祠挫,進(jìn)而訪問被保護(hù)的“臨界資源”那槽。下面的代碼演示了如何使用“鎖”來保護(hù)對(duì)銀行賬戶的操作,從而獲得正確的結(jié)果等舔。

from threading import Lock

class AccountWithLock(object):

    def __init__(self):
        self._balance = 0
        self._lock = Lock()

    def deposit(self, money):
        # 先獲取鎖才能執(zhí)行后續(xù)的代碼
        self._lock.acquire()
        try:
            new_balance = self._balance + money
            sleep(0.01)
            self._balance = new_balance
        finally:
            # 在finally中執(zhí)行釋放鎖的操作保證正常異常鎖都能釋放
            self._lock.release()

    @property
    def balance(self):
        return self._balance

def mainLock():
    account = AccountWithLock()
    thread_pool = []
    # 啟動(dòng)100個(gè)線程骚灸,并存1元錢
    for _ in range(100):
        t = AddMoney(account,1)
        thread_pool.append(t)
        t.start()
    
    for t in thread_pool:
        t.join()
    
    print('賬戶余額為:%d 元'% account.balance)

if __name__=='__main__':
    mainLock()
賬戶余額為:100 元

2. GIL 全局解釋器鎖 (面試常考)

比較遺憾的一件事情是Python的多線程并不能發(fā)揮CPU的多核特性慌植,這一點(diǎn)只要啟動(dòng)幾個(gè)執(zhí)行死循環(huán)的線程就可以得到證實(shí)了甚牲。
之所以如此,是因?yàn)镻ython的解釋器有一個(gè)“全局解釋器鎖”(GIL)的東西蝶柿,任何線程執(zhí)行前必須先獲得GIL鎖丈钙,然后每執(zhí)行100條字節(jié)碼,解釋器就自動(dòng)釋放GIL鎖交汤,讓別的線程有機(jī)會(huì)執(zhí)行雏赦,這是一個(gè)歷史遺留問題,但是即便如此芙扎,就如我們之前舉的例子星岗,使用多線程在提升執(zhí)行效率和改善用戶體驗(yàn)方面仍然是有積極意義的。

多進(jìn)程還是多線程選擇纵顾?

無論是多進(jìn)程還是多線程伍茄,只要數(shù)量一多,效率肯定上不去施逾,為什么呢敷矫?
我們打個(gè)比方例获,假設(shè)你不幸正在準(zhǔn)備中考,每天晚上需要做語文曹仗、數(shù)學(xué)榨汤、英語、物理怎茫、化學(xué)這5科的作業(yè)收壕,每項(xiàng)作業(yè)耗時(shí)1小時(shí)。如果你先花1小時(shí)做語文作業(yè)轨蛤,做完了蜜宪,再花1小時(shí)做數(shù)學(xué)作業(yè),這樣祥山,依次全部做完圃验,一共花5小時(shí),這種方式稱為單任務(wù)模型缝呕。如果你打算切換到多任務(wù)模型澳窑,可以先做1分鐘語文,再切換到數(shù)學(xué)作業(yè)供常,做1分鐘摊聋,再切換到英語,以此類推栈暇,只要切換速度足夠快麻裁,這種方式就和單核CPU執(zhí)行多任務(wù)是一樣的了,以旁觀者的角度來看源祈,你就正在同時(shí)寫5科作業(yè)悲立。

但是,切換作業(yè)是有代價(jià)的新博,比如從語文切到數(shù)學(xué),要先收拾桌子上的語文書本脚草、鋼筆(這叫保存現(xiàn)場(chǎng))赫悄,然后,打開數(shù)學(xué)課本馏慨、找出圓規(guī)直尺(這叫準(zhǔn)備新環(huán)境)埂淮,才能開始做數(shù)學(xué)作業(yè)。操作系統(tǒng)在切換進(jìn)程或者線程時(shí)也是一樣的写隶,它需要先保存當(dāng)前執(zhí)行的現(xiàn)場(chǎng)環(huán)境(CPU寄存器狀態(tài)、內(nèi)存頁等)痪蝇,然后,把新任務(wù)的執(zhí)行環(huán)境準(zhǔn)備好(恢復(fù)上次的寄存器狀態(tài)躏啰,切換內(nèi)存頁等)趁矾,才能開始執(zhí)行。這個(gè)切換過程雖然很快毫捣,但是也需要耗費(fèi)時(shí)間帝际。如果有幾千個(gè)任務(wù)同時(shí)進(jìn)行蔓同,操作系統(tǒng)可能就主要忙著切換任務(wù),根本沒有多少時(shí)間去執(zhí)行任務(wù)了蹲诀,這種情況最常見的就是硬盤狂響斑粱,點(diǎn)窗口無反應(yīng)侧甫,系統(tǒng)處于假死狀態(tài)。所以咒锻,多任務(wù)一旦多到一個(gè)限度守屉,反而會(huì)使得系統(tǒng)性能急劇下降,最終導(dǎo)致所有任務(wù)都做不好拇泛。


可以把任務(wù)分為計(jì)算密集型和I/O密集型。
計(jì)算密集型任務(wù)的特點(diǎn)是要進(jìn)行大量的計(jì)算恭取,消耗CPU資源熄守,比如對(duì)視頻進(jìn)行編碼解碼或者格式轉(zhuǎn)換等等,這種任務(wù)全靠CPU的運(yùn)算能力攒发,雖然也可以用多任務(wù)完成晋南,但是任務(wù)越多,花在任務(wù)切換的時(shí)間就越多偶妖,CPU執(zhí)行任務(wù)的效率就越低。計(jì)算密集型任務(wù)由于主要消耗CPU資源檀葛,這類任務(wù)用Python這樣的腳本語言去執(zhí)行效率通常很低腹缩,最能勝任這類任務(wù)的是C語言,我們之前提到了Python中有嵌入C/C++代碼的機(jī)制润讥。此類任務(wù)一般適合多進(jìn)程架構(gòu)盘寡。

除了計(jì)算密集型任務(wù),其他的涉及到網(wǎng)絡(luò)脆粥、存儲(chǔ)介質(zhì)I/O的任務(wù)都可以視為I/O密集型任務(wù)影涉,這類任務(wù)的特點(diǎn)是CPU消耗很少,任務(wù)的大部分時(shí)間都在等待I/O操作完成(因?yàn)镮/O的速度遠(yuǎn)遠(yuǎn)低于CPU和內(nèi)存的速度)匣缘。對(duì)于I/O密集型任務(wù)鲜棠,如果啟動(dòng)多任務(wù),就可以減少I/O等待時(shí)間從而讓CPU高效率的運(yùn)轉(zhuǎn)柑爸。此類任務(wù)一般適合多線程架構(gòu)盒音。


多進(jìn)程模式最大的優(yōu)點(diǎn)就是穩(wěn)定性高,因?yàn)橐粋€(gè)子進(jìn)程崩潰了,不會(huì)影響主進(jìn)程和其他子進(jìn)程用爪。(當(dāng)然主進(jìn)程掛了所有進(jìn)程就全掛了,但是Master進(jìn)程只負(fù)責(zé)分配任務(wù)诸衔,掛掉的概率低)著名的Apache最早就是采用多進(jìn)程模式。

多進(jìn)程模式的缺點(diǎn)是創(chuàng)建進(jìn)程的代價(jià)大就缆,在Unix/Linux系統(tǒng)下谒亦,用fork調(diào)用還行,在Windows下創(chuàng)建進(jìn)程開銷巨大切揭。另外锁摔,操作系統(tǒng)能同時(shí)運(yùn)行的進(jìn)程數(shù)也是有限的,在內(nèi)存和CPU的限制下谐腰,如果有幾千個(gè)進(jìn)程同時(shí)運(yùn)行,操作系統(tǒng)連調(diào)度都會(huì)成問題励背。

多線程模式通常比多進(jìn)程快一點(diǎn)桦踊,但是也快不到哪去,而且竟闪,多線程模式致命的缺點(diǎn)就是任何一個(gè)線程掛掉都可能直接造成整個(gè)進(jìn)程崩潰杖狼,因?yàn)樗芯€程共享進(jìn)程的內(nèi)存。在Windows上理朋,如果一個(gè)線程執(zhí)行的代碼出了問題绿聘,你經(jīng)常可以看到這樣的提示:“該程序執(zhí)行了非法操作兽愤,即將關(guān)閉”,其實(shí)往往是某個(gè)線程出了問題逐沙,但是操作系統(tǒng)會(huì)強(qiáng)制結(jié)束整個(gè)進(jìn)程吩案。

在Windows下帝簇,多線程的效率比多進(jìn)程要高崎岂,所以微軟的IIS服務(wù)器默認(rèn)采用多線程模式冲甘。由于多線程存在穩(wěn)定性的問題,IIS的穩(wěn)定性就不如Apache陶夜。為了緩解這個(gè)問題条辟,IIS和Apache現(xiàn)在又有多進(jìn)程+多線程的混合模式宏胯,真是把問題越搞越復(fù)雜肩袍。

異步IO

考慮到CPU和IO之間巨大的速度差異魂爪,一個(gè)任務(wù)在執(zhí)行的過程中大部分時(shí)間都在等待IO操作艰管,單進(jìn)程單線程模型會(huì)導(dǎo)致別的任務(wù)無法并行執(zhí)行,因此粗井,我們才需要多進(jìn)程模型或者多線程模型來支持多任務(wù)并發(fā)執(zhí)行。

現(xiàn)代操作系統(tǒng)對(duì)IO操作已經(jīng)做了巨大的改進(jìn),最大的特點(diǎn)就是支持異步IO餐济。如果充分利用操作系統(tǒng)提供的異步IO支持絮姆,就可以用單進(jìn)程單線程模型來執(zhí)行多任務(wù),這種全新的模型稱為事件驅(qū)動(dòng)模型篙悯,Nginx就是支持異步IO的Web服務(wù)器鸽照,它在單核CPU上采用單進(jìn)程模型就可以高效地支持多任務(wù)。在多核CPU上定血,可以運(yùn)行多個(gè)進(jìn)程(數(shù)量與CPU核心數(shù)相同)诞外,充分利用多核CPU。由于系統(tǒng)總的進(jìn)程數(shù)量十分有限茫虽,因此操作系統(tǒng)調(diào)度非常高效既们。用異步IO編程模型來實(shí)現(xiàn)多任務(wù)是一個(gè)主要的趨勢(shì)。

對(duì)應(yīng)到Python語言悼枢,單線程的異步編程模型稱為協(xié)程脾拆,有了協(xié)程的支持,就可以基于事件驅(qū)動(dòng)編寫高效的多任務(wù)程序绰上。我們會(huì)在后面討論如何編寫協(xié)程渠驼。

本文部分內(nèi)容來自于廖雪峰官方網(wǎng)站的《Python教程》

微信關(guān)注.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市爽哎,隨后出現(xiàn)的幾起案子器一,更是在濱河造成了極大的恐慌祈秕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件志鞍,死亡現(xiàn)場(chǎng)離奇詭異方仿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)玻孟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門黍翎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艳丛,“玉大人,你說我怎么就攤上這事碰酝〈鞑睿” “怎么了暖释?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長橄杨。 經(jīng)常有香客問我乡摹,道長趟卸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摹恨。我一直安慰自己,他們只是感情好寝凌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般泳赋。 火紅的嫁衣襯著肌膚如雪拣技。 梳的紋絲不亂的頭發(fā)上膏斤,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天毅访,我揣著相機(jī)與錄音,去河邊找鬼守呜。 笑死弥喉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的藻肄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼婆翔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起最蕾,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎慷嗜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菌赖,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辕羽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铣口。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖已艰,靈堂內(nèi)的尸體忽然破棺而出哩掺,到底是詐尸還是另有隱情盒件,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響俏脊,放射性物質(zhì)發(fā)生泄漏爷贫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望未巫。 院中可真熱鬧叙凡,春花似錦、人聲如沸饼拍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锋玲。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舀瓢,已是汗流浹背京髓。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诬烹。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓唬格,卻偏偏與公主長得像汰聋,于是被迫代替她去往敵國和親烹困。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乾吻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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

  • 1.多進(jìn)程與多線程介紹 / 區(qū)別 現(xiàn)在奢方,多核CPU已經(jīng)非常普及了袱巨,但是愉老,即使過去的單核CPU嫉入,也可以執(zhí)行多任務(wù)熬拒。由...
    Wayne_Dream閱讀 722評(píng)論 1 4
  • 前言:為什么有人說 Python 的多線程是雞肋,不是真正意義上的多線程欢瞪? 看到這里,也許你會(huì)疑惑骑祟。這很正常次企,所以...
    猴哥愛讀書閱讀 51,679評(píng)論 6 69
  • 1. 簡(jiǎn)介 用戶打開瀏覽器,其實(shí)就是打開了瀏覽器應(yīng)用程序也搓。那么什么是程序呢幔摸?我們常說瀏覽器是多線程的既忆,JS 是單線...
    love丁酥酥閱讀 3,519評(píng)論 0 6
  • 又來到了一個(gè)老生常談的問題宇挫,應(yīng)用層軟件開發(fā)的程序員要不要了解和深入學(xué)習(xí)操作系統(tǒng)呢翠储? 今天就這個(gè)問題開始,來談?wù)劜?..
    tangsl閱讀 4,125評(píng)論 0 23
  • 1.內(nèi)存的頁面置換算法 (1)最佳置換算法(OPT)(理想置換算法):從主存中移出永遠(yuǎn)不再需要的頁面;如無這樣的...
    杰倫哎呦哎呦閱讀 3,249評(píng)論 1 9