-
併發的方式
併發處理是同一時間段內有幾個程序都在一個cpu中處於運行狀態,但任一時刻只有一個程序在cpu上運行。
多進程、多線程、IO多路複用(通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。如select、poll、epoll)
-
進程
資源分配的基本單位,
Linux系統函數fork()可在父進程中創建一個子進程,在父進程接到新請求時,複製出一個子進程來處理,即父進程監控請求,子進程處理,實現併發處理。注意:必須是Linux系統,windows不能用fork。
-
進程的幾種狀態
(1)run(運行狀態):正在運行的進程或在等待隊列中等待的進程,等待的進程只要以得到cpu就可以運行
(2)Sleep(可中斷休眠狀態):相當於阻塞或在等待的狀態
(3)D(不可中斷休眠狀態):在磁盤上的進程
(4)T(停止狀態):這中狀態無法直觀的看見,因爲是進程停止後就釋放了資源,所以不會留在linux中
(5)Z(殭屍狀態):子進程先與父進程結束,但父進程沒有調用wait或waitpid來回收子進程的資源,所以子進程就成了殭屍進程,如果父進程結束後任然沒有回收子進程的資源,那麼1號進程將回收
-
線程
CPU調度和分配的基本單位,程序執行的最小單位。
在同一時間片只能有一個線程針對一個cpu執行指令,而且其他的線程必須被掛起。然後內核調度程序不斷的喚醒/掛起線程來模擬多個任務的執行。
一個進程可以由很多個線程組成,線程間共享進程的所有資源。線程有自己的堆棧、局部變量。
線程的創建調用pthread_create
線程中執行時一般都要進行同步和互斥,保證數據的一致性,因爲他們共享同一進程的所有資源。
同步:防止競爭(因同時修改導致數據的不一致)
互斥:使用互斥鎖防止多個線程同時讀寫某一塊內存區域。互斥鎖止允許一個線程進入臨界區。
信號量:內存區域只允許固定個數的線程進入,就要使用信號量,防止線程之間產生衝突。信號量許多個線程同時進入臨界區。
-
多線程的死鎖
定義:是指由於兩個或者多個線程互相持有對方所需要的資源,相互等待資源,處於僵持狀態
處理辦法:剝奪資源,或殺死其中一個線程
-
多進程與多線程的區別與聯繫
(1)單位:進程是資源分配的最小單位,線程是程序執行/CPU調度的最小單位。
(2)地址空間:進程有獨立地址空間(用於建立數據表來維護代碼段、堆棧段和數據段);線程使用相同的地址空間(共享進程中的數據)
(3)開銷:CPU切換線程及創建線程的開銷比進程小(不需要開闢新的地址空間)進程間切換代價大,線程間切換代價小
(4)通信:同一進程間的線程間由於共享進程的數據,通信更方便,難點在於處理同步與互斥;而進程間的通信需要以IPC(進程間通信InterProcess Communication,如管道、消息隊列、信號量、共享內存)的方式進行通信
(5)健壯性:多進程程序更加健壯(多線程程序只要有一個線程死掉,整個進程也死掉了,而一個進程死掉並不會對另外一個進程造成影響,因爲進程有自己獨立的地址空間。)
(6)結束的影響:進程的結束會將它所擁有的所有線程銷燬,線程的銷燬不影響同個進程下的線程
(7)私有屬性:“線程有自己的私有屬性TCB,線程id,寄存器、硬件上下文,而進程也有自己的私有屬性進程控制塊PCB,這些私有屬性是不被共享的,用來標示一個進程或一個線程的標誌”
-
協程、微線程
協程,又稱微線程,纖程,coroutine 。
協程是一個線程執行,兩個子過程通過相互協作完成某個任務。協程和子程序調用很像,但協程是在子程序內部中斷去執行別的子程序,適當時候返回接着執行,中斷有別於函數調用。
舉個例子(協程的運作):
-
協程與多線程的區別
(1)開銷。協程執行效率高,協程是單個線程執行,以子程序中斷的形式切換,沒有多線程切換的開銷。
(2)協程不需要多線程的鎖機制。不存在同時寫變量衝突。
-
使用 yield+協程 實現生產者-消費者模型
傳統的生產者-消費者模型是一個線程寫消息,一個線程取消息,使用鎖機制可能死鎖。如果改用協程,生產者生產消息後,直接通過yield跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼續生產。
yield一般配合send()方法使用:
send(value)將value值傳遞給yield表達式——下一次yield時返回值給send處——send——yield——。。。
程序:
def consumer():#消費者
r = ''#r初始爲空字符,啓動生成器後變成'200 0K'
while True:
n = yield r#接收send的value作爲n,下一次運行時返回r給produce中的r
if not n:#沒有生產時不能消費
return
print('[CONSUMER]Consuming %s...' % n)
r = '200 OK'
def producer(c):#生產者
c.send(None)#啓動生成器,啓動時必須value爲None
n = 0
while n < 5:
n = n + 1
print('[PRODUCER]Producing %s...' % n)
r = c.send(n)#send將value傳遞給yield表達式的值,返回下一次運行yield時的值
print('[PRODUCER]Consumer return: %s' % r)
c.close()#生產完5個就結束
c = consumer()
producer(c)
運行結果:
[PRODUCER]Producing 1...
[CONSUMER]Consuming 1...
[PRODUCER]Consumer return: 200 OK
[PRODUCER]Producing 2...
[CONSUMER]Consuming 2...
[PRODUCER]Consumer return: 200 OK
[PRODUCER]Producing 3...
[CONSUMER]Consuming 3...
[PRODUCER]Consumer return: 200 OK
[PRODUCER]Producing 4...
[CONSUMER]Consuming 4...
[PRODUCER]Consumer return: 200 OK
[PRODUCER]Producing 5...
[CONSUMER]Consuming 5...
[PRODUCER]Consumer return: 200 OK
運行過程:
運行分析:
step1:produce(c),運行produce,運行到c.send(None)——運行c即consumer(),啓動生成器,value=None,不需要把value給什麼yield接收
step2:運行consumer()時運行遇到n=yield r,返回r=''作爲n,切換回producer中的原send處
step3:producer中首輪生產者生產n=1,遇到r=c.send(n),將value=n傳給yield所在表達式作爲n,此時n=1,消費者消費n=1
step4:在consumer()的while True循環,下一次運行yield所在表達式,返回此時的r=‘200 OK’給r
step5:在producer()中進行while的循環,n+1後變成2,進行:“send 切換到yield 再次yield 切換回send”的循環
step6:循環直到n=5,不再進入while,執行c.close()結束
注意:
1.第一次啓動生成器c.send(None),value值爲None,啓動生成器時,調用c函數,不是到某個yield
2.send傳值是傳給了表達式,和yield後面是什麼沒有關係,再次遇到yield切換回原來send處。注意切換的位置以及傳回的是yield後面的值
3.傳回send是要把send的賦值表達式運行一次的,不是從send語句下面開始。
參考網址:
https://blog.csdn.net/zhou753099943/article/details/51771220
https://www.cnblogs.com/zhehan54/p/6130030.html
https://www.nowcoder.com/questionTerminal/234895a70e0b40e19db7f3fbaabc5fa3
https://blog.csdn.net/sunflowerduidui/article/details/51820067
https://www.cnblogs.com/zhang-can/p/7215506.html 這個鏈接講得比較深入,涉及到鎖/創建新線程/通信等等的程序,有需要可參考。