一、進(jìn)程
1、多任務(wù)原理
多任務(wù)是指操作系統(tǒng)同時(shí)可以運(yùn)行多個(gè)任務(wù)。
單核CPU實(shí)現(xiàn)多任務(wù)原理:操作系統(tǒng)輪流讓各個(gè)任務(wù)交替執(zhí)行;
多核CPU實(shí)現(xiàn)多任務(wù)原理:真正的執(zhí)行多任務(wù)只能在多核CPU上實(shí)現(xiàn),多出來的任務(wù)輪流調(diào)度到每個(gè)核心上執(zhí)行。
并發(fā):看上去一起執(zhí)行,任務(wù)數(shù)多于CPU核心數(shù);
并行:真正的一起執(zhí)行,任務(wù)數(shù)小于等于CPU核心數(shù)。
實(shí)現(xiàn)多任務(wù)的方式:
1、多進(jìn)程模式
2、多線程模式
3、協(xié)程模式
4、多進(jìn)程+多線程模式
2、進(jìn)程
對(duì)于操作系統(tǒng)而言,一個(gè)任務(wù)就是一個(gè)進(jìn)程;
進(jìn)程是系統(tǒng)中程序執(zhí)行和資源分配的基本單元,每個(gè)進(jìn)程都有自己的數(shù)據(jù)段、代碼段、堆棧段。
下面是一小段程序,一個(gè)單任務(wù)的例子。在其中,有兩個(gè)輸出語句分別在在兩個(gè)不同的循環(huán)當(dāng)中,單任務(wù)的執(zhí)行方式,也就是最初學(xué)習(xí)時(shí),當(dāng)一個(gè)循環(huán)沒有結(jié)束的時(shí)候,無法執(zhí)行到下面的程序當(dāng)中。如果想要讓兩個(gè)循環(huán)可以同時(shí)在執(zhí)行,就是在實(shí)現(xiàn)多任務(wù),當(dāng)然不是說同時(shí)輸出,而是兩個(gè)循環(huán)都在執(zhí)行著。
1 from time import sleep
2 # 只能執(zhí)行到那一個(gè)循環(huán),執(zhí)行不了run,所以叫單任務(wù)
3 def run():
4? ? while True:
5? ? ? ? print("&&&&&&&&&&&&&&&")
6? ? ? ? sleep(1.2)
7
8 if __name__ == "__main__":
9? ? while True:
10? ? ? ? print("**********")
11? ? ? ? sleep(1)
12? ? run()
接下來啟用多任務(wù),通過進(jìn)程來實(shí)現(xiàn)。
multiprocessing庫:跨平臺(tái)版本的多進(jìn)程模塊,提供了一個(gè)Process類來代表一個(gè)進(jìn)程對(duì)象(fork僅適用于Linux)。
下面的程序是在一個(gè)父進(jìn)程中創(chuàng)建一個(gè)子進(jìn)程,讓父進(jìn)程和子進(jìn)程可以都在執(zhí)行,創(chuàng)建方式程序中已經(jīng)很簡(jiǎn)潔了??梢宰约喊堰@兩段程序復(fù)制下來運(yùn)行一下,看看輸出的效果。
1 from multiprocessing import Process
2 from time import sleep
3 import os
4
5 def run(str):
6? ? # os.getpid()獲取當(dāng)前進(jìn)程id號(hào)
7? ? # os.getppid()獲取當(dāng)前進(jìn)程的父進(jìn)程id號(hào)
8? ? while True:
9? ? ? ? print("&&&&&&&&&&&&&&&%s--%s--%s" % (str, os.getpid(), os.getppid()))
10? ? ? ? sleep(0.5)
11
12 if __name__ == "__main__":
13? ? print("主(父)進(jìn)程啟動(dòng) %s" % (os.getpid()))
14? ? # 創(chuàng)建子進(jìn)程
15? ? # target說明進(jìn)程執(zhí)行的任務(wù)
16? ? p = Process(target=run, args=("nice",))
17? ? # 啟動(dòng)進(jìn)程
18? ? p.start()
19
20? ? while True:
21? ? ? ? print("**********")
22? ? ? ? sleep(1)
我想第一個(gè)單任務(wù)的程序就不必說了吧,就是一個(gè)死循環(huán),一直沒有執(zhí)行到下面的run函數(shù)。第二段程序是通過多進(jìn)程實(shí)現(xiàn)的多任務(wù),兩個(gè)循環(huán)都能執(zhí)行到,我把結(jié)果截圖放下面,最好自己去試一下。

3、父子進(jìn)程的先后順序
上面的多進(jìn)程的例子中輸出了那么多,我們使用的時(shí)候究竟是先執(zhí)行哪個(gè)后執(zhí)行哪個(gè)呢?根據(jù)我們的一般思維來說,我們寫的主函數(shù)其實(shí)就是父進(jìn)程,在主函數(shù)中間,要調(diào)用的也就是子進(jìn)程。
1 from multiprocessing import Process
2 from time import sleep
3 import os
4
5 def run():
6? ? print("啟動(dòng)子進(jìn)程")
7? ? print("子進(jìn)程結(jié)束")
8? ? sleep(3)
9
10 if __name__ == "__main__":
11? ? print("父進(jìn)程啟動(dòng)")
12? ? p = Process(target=run)
13? ? p.start()
14
15? ? # 父進(jìn)程的結(jié)束不能影響子進(jìn)程,讓進(jìn)程等待子進(jìn)程結(jié)束再執(zhí)行父進(jìn)程
16? ? p.join()
17
18? ? print("父進(jìn)程結(jié)束")

4、全局變量在多個(gè)進(jìn)程中不能共享?
在多進(jìn)程的程序當(dāng)中定義的全局變量在多個(gè)進(jìn)程中是不能共享的,篇幅較長(zhǎng)在這里就不舉例子了,可以自己試一下。這個(gè)也是和稍后要說的線程的一個(gè)區(qū)別,在線程中,變量是可以共享的,也因此衍生出一些問題,稍后再說。
5、啟動(dòng)多個(gè)進(jìn)程?
在正常工作使用的時(shí)候,當(dāng)然不止有有個(gè)一個(gè)兩個(gè)進(jìn)程,畢竟這一兩個(gè)也起不到想要的效果。那么就需要采用更多的進(jìn)程,這時(shí)候需要通過進(jìn)程池來實(shí)現(xiàn),就是在進(jìn)程池中放好你要建立的進(jìn)程,然后執(zhí)行的時(shí)候,把他們都啟動(dòng)起來,就可以同時(shí)進(jìn)行了,在一定的環(huán)境下可以大大的提高效率。當(dāng)然這個(gè)也和起初提到的有關(guān),如果你的CPU是單核的,那么多進(jìn)程也只是起到了讓幾個(gè)任務(wù)同時(shí)在執(zhí)行著,并沒有提高效率,而且啟動(dòng)進(jìn)程的時(shí)候還要花費(fèi)一些時(shí)間,因此在多核CPU當(dāng)中更能發(fā)揮優(yōu)勢(shì)。
在multiprocessing中有個(gè)Pool方法,可以實(shí)現(xiàn)進(jìn)程池。在利用進(jìn)程池時(shí)可以設(shè)置要啟動(dòng)幾個(gè)進(jìn)程,一般情況下,它默認(rèn)和你電腦的CPU核數(shù)一致,也可以自己設(shè)置,如果設(shè)置的進(jìn)程數(shù)多于CPU核數(shù),那多出來的進(jìn)程會(huì)輪流調(diào)度到每個(gè)核心上執(zhí)行。下面是啟動(dòng)多個(gè)進(jìn)程的過程。
1 from multiprocessing import Pool
2 import os
3 import time
4 import random
5
6
7 def run(name):
8? ? print("子進(jìn)程%s啟動(dòng)--%s" % (name, os.getpid()))
9? ? start = time.time()
10? ? time.sleep(random.choice([1,2,3,4,5]))
11? ? end = time.time()
12? ? print("子進(jìn)程%s結(jié)束--%s--耗時(shí)%.2f" % (name, os.getpid(), end-start))
13
14 if __name__ == "__main__":
15? ? print("啟動(dòng)父進(jìn)程")
16
17? ? # 創(chuàng)建多個(gè)進(jìn)程
18? ? # Pool 進(jìn)程池 :括號(hào)里的數(shù)表示可以同時(shí)執(zhí)行的進(jìn)程數(shù)量
19? ? # Pool()默認(rèn)大小是CPU核心數(shù)
20? ? pp = Pool(4)
21? ? for i in range(5):
22? ? ? ? # 創(chuàng)建進(jìn)程,放入進(jìn)程池,統(tǒng)一管理
23? ? ? ? pp.apply_async(run, args=(i,))
24
25? ? # 在調(diào)用join之前必須先調(diào)用close,調(diào)用close之后就不能再繼續(xù)添加新的進(jìn)程了
26? ? pp.close()
27? ? # 進(jìn)程池對(duì)象調(diào)用join還等待進(jìn)程池中所有的子進(jìn)程結(jié)束
28? ? pp.join()
29
30? ? print("結(jié)束父進(jìn)程")
6、文件拷貝(單進(jìn)程與多進(jìn)程對(duì)比)
(1)單進(jìn)程實(shí)現(xiàn)
View Code
(2)多進(jìn)程實(shí)現(xiàn)
View Code
上面兩個(gè)程序是兩種方法實(shí)現(xiàn)同一個(gè)目標(biāo)的程序,可以將其中的文件路徑更換為你自己的路徑,可以看到最后計(jì)算出的耗時(shí)是多少。也許有人發(fā)現(xiàn)并不是多進(jìn)程的效率就高,說的的確沒錯(cuò),因?yàn)閯?chuàng)建進(jìn)程也要花費(fèi)時(shí)間,沒準(zhǔn)啟動(dòng)進(jìn)程的時(shí)間遠(yuǎn)多讓這一個(gè)核心運(yùn)行所有核心用的時(shí)間要多。這個(gè)例子也只是演示一下如何使用,在大數(shù)據(jù)的任務(wù)下會(huì)有更深刻的體驗(yàn)。
?7、進(jìn)程對(duì)象
我們知道Python是一個(gè)面向?qū)ο蟮恼Z言。而且Python中萬物皆對(duì)象,進(jìn)程也可以封裝成對(duì)象,來方便以后自己使用,只要把他封裝的足夠豐富,提供清晰的接口,以后使用時(shí)會(huì)快捷很多,這個(gè)就根據(jù)自己的需求自己可以試一下,不寫了。
?8、進(jìn)程間通信
上面提到過進(jìn)程間的變量是不能共享的,那么如果有需要該怎么辦?通過隊(duì)列的方式進(jìn)行傳遞。在父進(jìn)程中創(chuàng)建隊(duì)列,然后把隊(duì)列傳到每個(gè)子進(jìn)程當(dāng)中,他們就可以共同對(duì)其進(jìn)行操作。?
1 from multiprocessing import Process, Queue
2 import os
3 import time
4
5
6 def write(q):
7? ? print("啟動(dòng)寫子進(jìn)程%s" % (os.getpid()))
8? ? for chr in ['A', 'B', 'C', 'D']:
9? ? ? ? q.put(chr)
10? ? ? ? time.sleep(1)
11? ? print("結(jié)束寫子進(jìn)程%s" % (os.getpid()))
12
13 def read(q):
14? ? print("啟動(dòng)讀子進(jìn)程%s" % (os.getpid()))
15? ? while True:
16? ? ? ? value = q.get()
17? ? ? ? print("value = "+value)
18? ? print("結(jié)束讀子進(jìn)程%s" % (os.getpid()))
19
20 if __name__ == "__main__":
21? ? # 父進(jìn)程創(chuàng)建隊(duì)列,并傳遞給子進(jìn)程
22? ? q = Queue()
23? ? pw = Process(target=write, args=(q,))
24? ? pr = Process(target=read, args=(q,))
25
26? ? pw.start()
27? ? pr.start()
28? ? # 寫進(jìn)程結(jié)束
29? ? pw.join()
30? ? # pr進(jìn)程里是個(gè)死循環(huán),無法等待期結(jié)束,只能強(qiáng)行結(jié)束
31? ? pr.terminate()
32? ? print("父進(jìn)程結(jié)束")
?二、線程
1、線程
在一個(gè)進(jìn)程內(nèi)部,要同時(shí)干多件事,就需要運(yùn)行多個(gè)"子任務(wù)",我們把進(jìn)程內(nèi)的多個(gè)"子任務(wù)"叫做線程
線程通常叫做輕型的進(jìn)程,線程是共享內(nèi)存空間,并發(fā)執(zhí)行的多任務(wù),每一個(gè)線程都共享一個(gè)進(jìn)程的資源
在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流圈:609164807 ?幫助突破瓶頸 提升思維能力
線程是最小的執(zhí)行單元而進(jìn)程由至少一個(gè)線程組成。如何調(diào)度進(jìn)程和線程,完全由操作系統(tǒng)來決定,程序自己不能決定什么時(shí)候執(zhí)行,執(zhí)行多長(zhǎng)時(shí)間
模塊:
1、_thread模塊 低級(jí)模塊(更接近底層)
2、threading模塊 高級(jí)模塊,對(duì)_thread進(jìn)行了封裝
2、啟動(dòng)一個(gè)線程
同樣,先給一個(gè)多線程的例子,其中,仍然使用run函數(shù)作為其中的一個(gè)子線程,主函數(shù)為父線程。通過threading的Thread方法創(chuàng)建線程并開啟,join來等待子線程。
1 import threading
2 import time
3
4
5 def run():
6? ? print("子線程(%s)啟動(dòng)" % (threading.current_thread().name))
7
8? ? # 實(shí)現(xiàn)線程的功能
9? ? time.sleep(1)
10? ? print("打印")
11? ? time.sleep(2)
12
13? ? print("子線程(%s)結(jié)束" % (threading.current_thread().name))
14
15
16 if __name__ == "__main__":
17? ? # 任何進(jìn)程都默認(rèn)會(huì)啟動(dòng)一個(gè)線程,稱為主線程,主線程可以啟動(dòng)新的子線程
18? ? # current_thread():返回線程的實(shí)例
19? ? print("主線程(%s)啟動(dòng)" % (threading.current_thread().name))
20
21? ? # 創(chuàng)建子線程
22? ? t = threading.Thread(target=run, name="runThread")
23? ? t.start()
24
25? ? # 等待線程結(jié)束
26? ? t.join()
27
28? ? print("主線程(%s)結(jié)束" % (threading.current_thread().name))
3、線程間數(shù)據(jù)共享
多線程和多進(jìn)程最大的不同在于,多進(jìn)程中,同一個(gè)變量,各自有一份拷貝存在每個(gè)進(jìn)程中,互不影響。
而多線程所有變量都由所有線程共享。所以任何一個(gè)變量都可以被任何一個(gè)線程修改,因此,線程之間共享數(shù)據(jù)最大的危險(xiǎn)在于多個(gè)線程同時(shí)修改一個(gè)變量,容易把內(nèi)容改亂了。
1 import threading
2
3
4 num = 10
5
6 def run(n):
7? ? global num
8? ? for i in range(10000000):
9? ? ? ? num = num + n
10? ? ? ? num = num - n
11
12 if __name__ == "__main__":
13? ? t1 = threading.Thread(target=run, args=(6,))
14? ? t2 = threading.Thread(target=run, args=(9,))
15
16? ? t1.start()
17? ? t2.start()
18? ? t1.join()
19? ? t2.join()
20
21? ? print("num = ",num)
4、線程鎖
在第三小點(diǎn)中已經(jīng)提到了,多線程的一個(gè)缺點(diǎn)就是數(shù)據(jù)是共享的,如果有兩個(gè)線程正同時(shí)在修改這個(gè)數(shù)據(jù),就會(huì)出現(xiàn)混亂,它自己也不知道該聽誰的了,尤其是在運(yùn)算比較復(fù)雜,次數(shù)較多的時(shí)候,這種錯(cuò)誤的機(jī)會(huì)會(huì)更大。
當(dāng)然,解決辦法也是有的,那就是利用線程鎖。加鎖的意思就是在其中一個(gè)線程正在對(duì)數(shù)據(jù)進(jìn)行操作時(shí),讓其他線程不得介入。這個(gè)加鎖和釋放鎖是由人來確定的。
確保了這段代碼只能由一個(gè)線程從頭到尾的完整執(zhí)行
阻止了多線程的并發(fā)執(zhí)行,要比不加鎖時(shí)候效率低。包含鎖的代碼段只能以單線程模式執(zhí)行
由于可以存在多個(gè)鎖,不同線程持有不同的鎖,并試圖獲取其他的鎖,可能造成死鎖導(dǎo)致多個(gè)線程掛起,只能靠操作系統(tǒng)強(qiáng)制終止
1 def run(n):
2? ? global num
3? ? for i in range(10000000):? ?
4? ? ? ? lock.acquire()
5? ? ? ? try:
6? ? ? ? ? ? num = num + n
7? ? ? ? ? ? num = num - n
8? ? ? ? finally:
9? ? ? ? ? ? # 修改完釋放鎖
10? ? ? ? ? ? lock.release()
11
12 if __name__ == "__main__":
13? ? t1 = threading.Thread(target=run, args=(6,))
14? ? t2 = threading.Thread(target=run, args=(9,))
15
16? ? t1.start()
17? ? t2.start()
18? ? t1.join()
19? ? t2.join()
20
21? ? print("num = ",num)
上面這段程序是循環(huán)多次num+n-n+n-n的過程,變量n分別設(shè)為6和9是在兩個(gè)不同的線程當(dāng)中,程序中已經(jīng)加了鎖,你可以先去掉試一下,當(dāng)循環(huán)次數(shù)較小的時(shí)候也許還能正確,但次數(shù)一旦取的較高就會(huì)出現(xiàn)混亂。
加鎖是在循環(huán)體當(dāng)中,依次執(zhí)行加減法,定義中說到確保一個(gè)線程從頭到尾的完整執(zhí)行,也就是在計(jì)算途中,不會(huì)有其他的線程打擾。你可以想一下,如果一個(gè)線程執(zhí)行完加法,正在執(zhí)行減法,另一個(gè)線程進(jìn)來了,它要先進(jìn)行加法時(shí)的初始sum值該是多少呢,線程二不一定在線程一的什么時(shí)候進(jìn)來,萬一剛進(jìn)來時(shí)候,線程一恰好給sum賦值了,而線程二仍然用的是正準(zhǔn)備進(jìn)來時(shí)候的sum值,那從這里開始豈不已經(jīng)分道揚(yáng)鑣了。所以,運(yùn)算的次數(shù)越多,結(jié)果會(huì)越離譜。
這個(gè)說完了,還有一個(gè)小小的改進(jìn)。你是否記得讀寫文件時(shí)候書寫的一種簡(jiǎn)便形式,通過with來實(shí)現(xiàn),可以避免我們忘記關(guān)閉文件,自動(dòng)幫我們關(guān)閉。當(dāng)然還有一些其他地方也用到了這個(gè)方法。這里也同樣適用。
1 # 與上面代碼功能相同,with lock可以自動(dòng)上鎖與解鎖
2 with lock:
3? ? num = num + n
4? ? num = num - n
5、ThreadLocal
創(chuàng)建一個(gè)全局的ThreadLocal對(duì)象
每個(gè)線程有獨(dú)立的存儲(chǔ)空間
每個(gè)線程對(duì)ThreadLocal對(duì)象都可以讀寫,但是互不影響
根據(jù)名字也可以看出,也就是在本地建個(gè)連接,所有的操作在本地進(jìn)行,每個(gè)線程之間沒有數(shù)據(jù)的影響。
1 import threading
2
3
4 num = 0
5 local = threading.local()
6
7 def run(x, n):
8? ? x = x + n
9? ? x = x - n
10
11 def func(n):
12? ? # 每個(gè)線程都有l(wèi)ocal.x
13? ? local.x = num
14? ? for i in range(10000000):
15? ? ? ? run(local.x, n)
16? ? print("%s-%d" % (threading.current_thread().name, local.x))
17
18
19 if __name__ == "__main__":
20? ? t1 = threading.Thread(target=func, args=(6,))
21? ? t2 = threading.Thread(target=func, args=(9,))
22
23? ? t1.start()
24? ? t2.start()
25? ? t1.join()
26? ? t2.join()
27
28? ? print("num = ",num)
6、控制線程數(shù)量
1 '''
2 控制線程數(shù)量是指控制線程同時(shí)觸發(fā)的數(shù)量,可以拿下來這段代碼運(yùn)行一下,下面啟動(dòng)了5個(gè)線程,但是他們會(huì)兩個(gè)兩個(gè)的進(jìn)行
????在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流圈:609164807 ?幫助突破瓶頸 提升思維能力
3 '''
4 import threading
5 import time
6
7 # 控制并發(fā)執(zhí)行線程的數(shù)量
8 sem = threading.Semaphore(2)
9
10 def run():
11? ? with sem:
12? ? ? ? for i in range(10):
13? ? ? ? ? ? print("%s---%d" % (threading.current_thread().name, i))
14? ? ? ? ? ? time.sleep(1)
15
16
17 if __name__ == "__main__":
18? ? for i in range(5):
19? ? ? ? threading.Thread(target=run).start()
上面的程序是有多個(gè)線程,但是每次限制同時(shí)執(zhí)行的線程,通俗點(diǎn)說就是限制并發(fā)線程的上限;除此之外,也可以限制線程數(shù)量的下限,也就是至少達(dá)到多少個(gè)線程才能觸發(fā)。
1 import threading
2 import time
3
4
5 # 湊夠一定數(shù)量的線程才會(huì)執(zhí)行,否則一直等著
6 bar = threading.Barrier(4)
7
8 def run():
9? ? print("%s--start" % (threading.current_thread().name))
10? ? time.sleep(1)
11? ? bar.wait()
12? ? print("%s--end" % (threading.current_thread().name))
13
14
15 if __name__ == "__main__":
16? ? for i in range(5):
17? ? ? ? threading.Thread(target=run).start()
7、定時(shí)線程
1 import threading
2
3
4 def run():
5? ? print("***********************")
6
7 # 延時(shí)執(zhí)行線程
8 t = threading.Timer(5, run)
9 t.start()
10
11 t.join()
12 print("父線程結(jié)束")
8、線程通信
1 import threading
2 import time
3
4
5 def func():
6? ? # 事件對(duì)象
7? ? event = threading.Event()
8? ? def run():
9? ? ? ? for i in range(5):
10? ? ? ? ? ? # 阻塞,等待事件的觸發(fā)
11? ? ? ? ? ? event.wait()
12? ? ? ? ? ? # 重置阻塞,使后面繼續(xù)阻塞
13? ? ? ? ? ? event.clear()
14? ? ? ? ? ? print("**************")
15? ? t = threading.Thread(target=run).start()
16? ? return event
17
18 e = func()
19
20 # 觸發(fā)事件
21 for i in range(5):
22? ? time.sleep(2)
23? ? e.set()
9、一個(gè)小栗子
這個(gè)例子是用了生產(chǎn)者和消費(fèi)者來模擬,要進(jìn)行數(shù)據(jù)通信,還引入了隊(duì)列。先來理解一下。
1 import threading
2 import queue
3 import time
4 import random
5
6
7 # 生產(chǎn)者
8 def product(id, q):
9? ? while True:
10? ? ? ? num = random.randint(0, 10000)
11? ? ? ? q.put(num)
12? ? ? ? print("生產(chǎn)者%d生產(chǎn)了%d數(shù)據(jù)放入了隊(duì)列" % (id, num))
13? ? ? ? time.sleep(3)
14? ? # 任務(wù)完成
15? ? q.task_done()
16
17 # 消費(fèi)者
18 def customer(id, q):
19? ? while True:
20? ? ? ? item = q.get()
21? ? ? ? if item is None:
22? ? ? ? ? ? break
23? ? ? ? print("消費(fèi)者%d消費(fèi)了%d數(shù)據(jù)" % (id, item))
24? ? ? ? time.sleep(2)
25? ? # 任務(wù)完成
26? ? q.task_done()
27
28
29 if __name__ == "__main__":
30? ? # 消息隊(duì)列
31? ? q = queue.Queue()
32
33? ? # 啟動(dòng)生產(chǎn)者
34? ? for i in range(4):
35? ? ? ? threading.Thread(target=product, args=(i, q)).start()
36
37? ? # 啟動(dòng)消費(fèi)者
38? ? for i in range(3):
39? ? ? ? threading.Thread(target=customer, args=(i, q)).start()
10、線程調(diào)度
1 import threading
2 import time
3
4
5 # 線程條件變量
6 cond = threading.Condition()
7
8
9 def run():
10? ? with cond:
11? ? ? ? for i in range(0, 10, 2):
12? ? ? ? ? ? print(threading.current_thread().name, i)
13? ? ? ? ? ? time.sleep(1)
14? ? ? ? ? ? cond.wait()? # 阻塞
15? ? ? ? ? ? cond.notify()? # 告訴另一個(gè)線程可以執(zhí)行
16
17
18 def run2():
19? ? with cond:
20? ? ? ? for i in range(1, 10, 2):
21? ? ? ? ? ? print(threading.current_thread().name, i)
22? ? ? ? ? ? time.sleep(1)
23? ? ? ? ? ? cond.notify()
24? ? ? ? ? ? cond.wait()
25
26
27 threading.Thread(target=run).start()
28 threading.Thread(target=run2).start()
三、協(xié)程
1、協(xié)程
子程序/子函數(shù):在所有語言中都是層級(jí)調(diào)用,比如A調(diào)用B,在B執(zhí)行的工程中又可以調(diào)用C,C執(zhí)行完畢返回,B執(zhí)行完畢返回最后是A執(zhí)行完畢。是通過棧實(shí)現(xiàn)的,一個(gè)線程就是一個(gè)子程序,子程序調(diào)用總是一個(gè)入口,一次返回,調(diào)用的順序是明確的
協(xié)程:看上去也是子程序,但執(zhí)行過程中,在子程序的內(nèi)部可中斷,然后轉(zhuǎn)而執(zhí)行別的子程序,不是函數(shù)調(diào)用,有點(diǎn)類似CPU中斷
1 # 這是一個(gè)子程序的調(diào)用
2 def C():
3? ? print("C--start")
4? ? print("C--end")
5
6 def B():
7? ? print("B--start")
8? ? C()
9? ? print("B--end")
10
11 def A():
12? ? print("A--start")
13? ? B()
14? ? print("A--end")
15
16 A()
協(xié)程與子程序調(diào)用的結(jié)果類似,但不是通過在函數(shù)中調(diào)用另一個(gè)函數(shù)
在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流圈:609164807 ?幫助突破瓶頸 提升思維能力
協(xié)程執(zhí)行起來有點(diǎn)像線程,但協(xié)程的特點(diǎn)在于是一個(gè)線程
與線程相比的優(yōu)點(diǎn):協(xié)程的執(zhí)行效率極高,因?yàn)橹挥幸粋€(gè)線程,也不存在同時(shí)寫變量的沖突,在協(xié)程中共享資源不加鎖,只需要判斷狀態(tài)
2、協(xié)程的原理
1 # python對(duì)協(xié)程的支持是通過generator實(shí)現(xiàn)的
2 def run():
3? ? print(1)
4? ? yield 10
5? ? print(2)
6? ? yield 20
7? ? print(3)
8? ? yield 30
9
10 # 協(xié)程的最簡(jiǎn)單風(fēng)格,控制函數(shù)的階段執(zhí)行,節(jié)約線程或者進(jìn)程的切換
11 # 返回值是一個(gè)生成器
12 m = run()
13 print(next(m))
14 print(next(m))
15 print(next(m))
3、數(shù)據(jù)傳輸
1 # python對(duì)協(xié)程的支持是通過generator實(shí)現(xiàn)的
2 def run():
3? ? print(1)
4? ? yield 10
5? ? print(2)
6? ? yield 20
7? ? print(3)
8? ? yield 30
9
10 # 協(xié)程的最簡(jiǎn)單風(fēng)格,控制函數(shù)的階段執(zhí)行,節(jié)約線程或者進(jìn)程的切換
11 # 返回值是一個(gè)生成器
12 m = run()
13 print(next(m))
14 print(next(m))
15 print(next(m))
4、小栗子
1 def product(c):
2? ? c.send(None)
3? ? for i in range(5):
4? ? ? ? print("生產(chǎn)者產(chǎn)生數(shù)據(jù)%d" % (i))
5? ? ? ? r = c.send(str(i))
6? ? ? ? print("消費(fèi)者消費(fèi)了數(shù)據(jù)%s" % (r))
7? ? c.close()
8
9
10 def customer():
11? ? data = ""
12? ? while True:
13? ? ? ? n = yield data
14? ? ? ? if not n:
15? ? ? ? ? ? return
16? ? ? ? print("消費(fèi)者消費(fèi)了%s" % (n))
17? ? ? ? data = "200"
18
19
20 c = customer()
21 product(c)