進(jìn)程、線程和協(xié)程

一、進(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)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在前面的話 代碼中的# > 表示的是輸出結(jié)果 輸入 使用input()函數(shù) 用法 注意input函數(shù)輸出的均是字...
    FlyingLittlePG閱讀 3,207評(píng)論 0 9
  • 0三、線程和進(jìn)程的關(guān)系1、一個(gè)進(jìn)程可以有多個(gè)線程,但至少有一個(gè)線程;而一個(gè)線程只能在一個(gè)進(jìn)程的地址空間內(nèi)活動(dòng)。2、...
    十二右閱讀 2,868評(píng)論 0 0
  • 1.進(jìn)程和線程 隊(duì)列:1、進(jìn)程之間的通信: q = multiprocessing.Queue()2、...
    一只寫程序的猿閱讀 1,233評(píng)論 0 17
  • 你的脾氣里藏著你讀過的書,走過的路,愛過的人! 小兒喜怒無常,哭笑無常不由人;少年怪誕不羈,喜惡無律事隨心;青年激...
    獨(dú)步天涯客閱讀 863評(píng)論 0 1
  • 讀書本身是一件輸入的過程,可是我覺得輸出的重要性更重要。因?yàn)檩敵龅膶W(xué)習(xí)方式比輸入的效率更高,當(dāng)我們能夠?qū)⑺吹降?..
    冬江花月夜閱讀 42,374評(píng)論 0 4

友情鏈接更多精彩內(nèi)容