日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯網營銷解決方案
Python多線程、多進程詳細整理

線程與進程的區(qū)別

進程(process)和線程(thread)是操作系統(tǒng)的基本概念,但是它們比較抽象,不容易掌握。關于多進程和多線程,教科書上最經典的一句話是“進程是資源分配的最小單位,線程是CPU調度的最小單位”。線程是程序中一個單一的順序控制流程。進程內一個相對獨立的、可調度的執(zhí)行單元,是系統(tǒng)獨立調度和分派CPU的基本單位指運行中的程序的調度單位。在單個程序中同時運行多個線程完成不同的工作,稱為多線程。

進程和線程區(qū)別

進程是資源分配的基本單位。所有與該進程有關的資源,都被記錄在進程控制塊PCB中。以表示該進程擁有這些資源或正在使用它們。另外,進程也是搶占處理機的調度單位,它擁有一個完整的虛擬地址空間。當進程發(fā)生調度時,不同的進程擁有不同的虛擬地址空間,而同一進程內的不同線程共享同一地址空間。

與進程相對應,線程與資源分配無關,它屬于某一個進程,并與進程內的其他線程一起共享進程的資源。線程只由相關堆棧(系統(tǒng)?;蛴脩魲#┘拇嫫骱途€程控制表TCB組成。寄存器可被用來存儲線程內的局部變量,但不能存儲其他線程的相關變量。

通常在一個進程中可以包含若干個線程,它們可以利用進程所擁有的資源。在引入線程的操作系統(tǒng)中,通常都是把進程作為分配資源的基本單位,而把線程作為獨立運行和獨立調度的基本單位。

由于線程比進程更小,基本上不擁有系統(tǒng)資源,故對它的調度所付出的開銷就會小得多,能更高效的提高系統(tǒng)內多個程序間并發(fā)執(zhí)行的程度,從而顯著提高系統(tǒng)資源的利用率和吞吐量。

因而近年來推出的通用操作系統(tǒng)都引入了線程,以便進一步提高系統(tǒng)的并發(fā)性,并把它視為現代操作系統(tǒng)的一個重要指標。

線程與進程的區(qū)別可以歸納為以下4點:

  • 地址空間和其它資源(如打開文件):進程間相互獨立,同一進程的各線程間共享。某進程內的線程在其它進程不可見。
  • 通信:進程間通信IPC,線程間可以直接讀寫進程數據段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數據的一致性。
  • 調度和切換:線程上下文切換比進程上下文切換要快得多。
  • 在多線程OS中,進程不是一個可執(zhí)行的實體。

多進程和多線程的比較

對比維度

多進程

多線程

總結

數據共享、同步

數據共享復雜,同步簡單

數據共享簡單,同步復雜

各有優(yōu)劣

內存、CPU

占用內存多,切換復雜,CPU利用率低

占用內存少,切換簡單,CPU利用率高

線程占優(yōu)

創(chuàng)建、銷毀、切換

復雜,速度慢

簡單,速度快

線程占優(yōu)

編程、調試

編程簡單,調試簡單

編程復雜,調試復雜

進程占優(yōu)

可靠性

進程間不會互相影響

一個線程掛掉將導致整個進程掛掉

進程占優(yōu)

分布式

適用于多核、多機,擴展到多臺機器簡單

適合于多核

進程占優(yōu)

總結,進程和線程還可以類比為火車和車廂:

  • 線程在進程下行進(單純的車廂無法運行)
  • 一個進程可以包含多個線程(一輛火車可以有多個車廂)
  • 不同進程間數據很難共享(一輛火車上的乘客很難換到另外一輛火車,比如站點換乘)
  • 同一進程下不同線程間數據很易共享(A車廂換到B車廂很容易)
  • 進程要比線程消耗更多的計算機資源(采用多列火車相比多個車廂更耗資源)
  • 進程間不會相互影響,一個線程掛掉將導致整個進程掛掉(一列火車不會影響到另外一列火車,但是如果一列火車上中間的一節(jié)車廂著火了,將影響到該趟火車的所有車廂)
  • 進程可以拓展到多機,進程最多適合多核(不同火車可以開在多個軌道上,同一火車的車廂不能在行進的不同的軌道上)
  • 進程使用的內存地址可以上鎖,即一個線程使用某些共享內存時,其他線程必須等它結束,才能使用這一塊內存。(比如火車上的洗手間)-”互斥鎖(mutex)”
  • 進程使用的內存地址可以限定使用量(比如火車上的餐廳,最多只允許多少人進入,如果滿了需要在門口等,等有人出來了才能進去)-“信號量(semaphore)”

Python全局解釋器鎖GIL

全局解釋器鎖(英語:Global Interpreter Lock,縮寫GIL),并不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。由于CPython是大部分環(huán)境下默認的Python執(zhí)行環(huán)境。所以在很多人的概念里CPython就是Python,也就想當然的把GIL歸結為Python語言的缺陷。那么CPython實現中的GIL又是什么呢?來看看官方的解釋:

 The mechanism used by the CPython interpreter to assure that only one thread executes Python bytecode at a time. This simplifies the CPython implementation by making the object model (including critical built-in types such as dict) implicitly safe against concurrent access. Locking the entire interpreter makes it easier for the interpreter to be multi-threaded, at the expense of much of the parallelism afforded by multi-processor machines.

Python代碼的執(zhí)行由Python 虛擬機(也叫解釋器主循環(huán),CPython版本)來控制,Python 在設計之初就考慮到要在解釋器的主循環(huán)中,同時只有一個線程在執(zhí)行,即在任意時刻,只有一個線程在解釋器中運行。對Python 虛擬機的訪問由全局解釋器鎖(GIL)來控制,正是這個鎖能保證同一時刻只有一個線程在運行。

GIL 有什么好處?簡單來說,它在單線程的情況更快,并且在和 C 庫結合時更方便,而且不用考慮線程安全問題,這也是早期 Python 最常見的應用場景和優(yōu)勢。另外,GIL的設計簡化了CPython的實現,使得對象模型,包括關鍵的內建類型如字典,都是隱含可以并發(fā)訪問的。鎖住全局解釋器使得比較容易的實現對多線程的支持,但也損失了多處理器主機的并行計算能力。

在多線程環(huán)境中,Python 虛擬機按以下方式執(zhí)行:

  1. 設置GIL
  2. 切換到一個線程去運行
  3. 運行直至指定數量的字節(jié)碼指令,或者線程主動讓出控制(可以調用sleep(0))
  4. 把線程設置為睡眠狀態(tài)
  5. 解鎖GIL
  6. 再次重復以上所有步驟

Python3.2前,GIL的釋放邏輯是當前線程遇見IO操作或者ticks計數達到100(ticks可以看作是python自身的一個計數器,專門做用于GIL,每次釋放后歸零,這個計數可以通過 sys.setcheckinterval 來調整),進行釋放。因為計算密集型線程在釋放GIL之后又會立即去申請GIL,并且通常在其它線程還沒有調度完之前它就已經重新獲取到了GIL,就會導致一旦計算密集型線程獲得了GIL,那么它在很長一段時間內都將占據GIL,甚至一直到該線程執(zhí)行結束。

Python 3.2開始使用新的GIL。新的GIL實現中用一個固定的超時時間來指示當前的線程放棄全局鎖。在當前線程保持這個鎖,且其他線程請求這個鎖時,當前線程就會在5毫秒后被強制釋放該鎖。該改進在單核的情況下,對于單個線程長期占用GIL的情況有所好轉。

在單核CPU上,數百次的間隔檢查才會導致一次線程切換。在多核CPU上,存在嚴重的線程顛簸(thrashing)。而每次釋放GIL鎖,線程進行鎖競爭、切換線程,會消耗資源。單核下多線程,每次釋放GIL,喚醒的那個線程都能獲取到GIL鎖,所以能夠無縫執(zhí)行,但多核下,CPU0釋放GIL后,其他CPU上的線程都會進行競爭,但GIL可能會馬上又被CPU0拿到,導致其他幾個CPU上被喚醒后的線程會醒著等待到切換時間后又進入待調度狀態(tài),這樣會造成線程顛簸(thrashing),導致效率更低。

另外,從上面的實現機制可以推導出,Python的多線程對IO密集型代碼要比CPU密集型代碼更加友好。

針對GIL的應對措施:

  • 使用更高版本Python(對GIL機制進行了優(yōu)化)
  • 使用多進程替換多線程(多進程之間沒有GIL,但是進程本身的資源消耗較多)
  • 指定cpu運行線程(使用affinity模塊)
  • 使用Jython、IronPython等無GIL解釋器
  • 全IO密集型任務時才使用多線程
  • 使用協程(高效的單線程模式,也稱微線程;通常與多進程配合使用)
  •  將關鍵組件用C/C++編寫為Python擴展,通過ctypes使Python程序直接調用C語言編譯的動態(tài)鏈接庫的導出函數。(with nogil調出GIL限制)

Python的多進程包multiprocessing

Python的threading包主要運用多線程的開發(fā),但由于GIL的存在,Python中的多線程其實并不是真正的多線程,如果想要充分地使用多核CPU的資源,大部分情況需要使用多進程。在Python 2.6版本的時候引入了multiprocessing包,它完整的復制了一套threading所提供的接口方便遷移。唯一的不同就是它使用了多進程而不是多線程。每個進程有自己的獨立的GIL,因此也不會出現進程之間的GIL爭搶。

借助這個multiprocessing,你可以輕松完成從單進程到并發(fā)執(zhí)行的轉換。multiprocessing支持子進程、通信和共享數據、執(zhí)行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。

Multiprocessing產生的背景

除了應對Python的GIL以外,產生multiprocessing的另外一個原因時Windows操作系統(tǒng)與Linux/Unix系統(tǒng)的不一致。

Unix/Linux操作系統(tǒng)提供了一個fork()系統(tǒng)調用,它非常特殊。普通的函數,調用一次,返回一次,但是fork()調用一次,返回兩次,因為操作系統(tǒng)自動把當前進程(父進程)復制了一份(子進程),然后,分別在父進程和子進程內返回。子進程永遠返回0,而父進程返回子進程的ID。這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調用getpid()就可以拿到父進程的ID。

Python的os模塊封裝了常見的系統(tǒng)調用,其中就包括fork,可以在Python程序中輕松創(chuàng)建子進程:

import os
print('Process (%s) start...' % os.getpid())
\# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))

上述代碼在Linux、Unix和Mac上的執(zhí)行結果為:

Process (876) start...
I (876) just created a child process (877).
I am child process (877) and my parent is 876.

有了fork調用,一個進程在接到新任務時就可以復制出一個子進程來處理新任務,常見的Apache服務器就是由父進程監(jiān)聽端口,每當有新的http請求時,就fork出子進程來處理新的http請求。

由于Windows沒有fork調用,上面的代碼在Windows上無法運行。由于Python是跨平臺的,自然也應該提供一個跨平臺的多進程支持。multiprocessing模塊就是跨平臺版本的多進程模塊。multiprocessing模塊封裝了fork()調用,使我們不需要關注fork()的細節(jié)。由于Windows沒有fork調用,因此,multiprocessing需要“模擬”出fork的效果。

multiprocessing常用組件及功能

創(chuàng)建管理進程模塊:

  • Process(用于創(chuàng)建進程)
  • Pool(用于創(chuàng)建管理進程池)
  • Queue(用于進程通信,資源共享)
  •  Value,Array(用于進程通信,資源共享)
  • Pipe(用于管道通信)
  • Manager(用于資源共享)

同步子進程模塊:

  • Condition(條件變量)
  • Event(事件)
  • Lock(互斥鎖)
  • RLock(可重入的互斥鎖(同一個進程可以多次獲得它,同時不會造成阻塞)
  • Semaphore(信號量)

接下來就一起來學習下每個組件及功能的具體使用方法。

Process(用于創(chuàng)建進程)

multiprocessing模塊提供了一個Process類來代表一個進程對象。

在multiprocessing中,每一個進程都用一個Process類來表示。

構造方法:Process([group [, target [, name [, args [, kwargs]]]]])

  • group:分組,實際上不使用,值始終為None
  • target:表示調用對象,即子進程要執(zhí)行的任務,你可以傳入方法名
  • name:為子進程設定名稱
  • args:要傳給target函數的位置參數,以元組方式進行傳入。
  • kwargs:要傳給target函數的字典參數,以字典方式進行傳入。

實例方法:

  • start():啟動進程,并調用該子進程中的p.run()
  • run():進程啟動時運行的方法,正是它去調用target指定的函數,我們自定義類的類中一定要實現該方法
  • terminate():強制終止進程p,不會進行任何清理操作,如果p創(chuàng)建了子進程,該子進程就成了僵尸進程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那么也將不會被釋放,進而導致死鎖
  • is_alive():返回進程是否在運行。如果p仍然運行,返回True
  • join([timeout]):進程同步,主進程等待子進程完成后再執(zhí)行后面的代碼。線程等待p終止(強調:是主線程處于等的狀態(tài),而p是處于運行的狀態(tài))。timeout是可選的超時時間(超過這個時間,父線程不再等待子線程,繼續(xù)往下執(zhí)行),需要強調的是,p.join只能join住start開啟的進程,而不能join住run開啟的進程

屬性介紹:

  •  daemon:默認值為False,如果設為True,代表p為后臺運行的守護進程;當p的父進程終止時,p也隨之終止,并且設定為True后,p不能創(chuàng)建自己的新進程;必須在p.start()之前設置
  • name:進程的名稱
  • pid:進程的pid
  • exitcode:進程在運行時為None、如果為–N,表示被信號N結束(了解即可)
  •  authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是為涉及網絡連接的底層進程間通信提供安全性,這類連接只有在具有相同的身份驗證鍵時才能成功(了解即可)

使用示例:(注意:在windows中Process()必須放到if name == ‘main’:下)

from multiprocessing import Process
import os
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')

Pool(用于創(chuàng)建管理進程池)

Pool類用于需要執(zhí)行的目標很多,而手動限制進程數量又太繁瑣時,如果目標少且不用控制進程數量則可以用Process類。Pool可以提供指定數量的進程,供用戶調用,當有新的請求提交到Pool中時,如果池還沒有滿,那么就會創(chuàng)建一個新的進程用來執(zhí)行該請求;但如果池中的進程數已經達到規(guī)定最大值,那么該請求就會等待,直到池中有進程結束,就重用進程池中的進程。

構造方法:Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])

  • processes :要創(chuàng)建的進程數,如果省略,將默認使用cpu_count()返回的數量。
  • initializer:每個工作進程啟動時要執(zhí)行的可調用對象,默認為None。如果initializer是None,那么每一個工作進程在開始的時候會調用initializer(*initargs)。
  • initargs:是要傳給initializer的參數組。
  • maxtasksperchild:工作進程退出之前可以完成的任務數,完成后用一個新的工作進程來替代原進程,來讓閑置的資源被釋放。maxtasksperchild默認是None,意味著只要Pool存在工作進程就會一直存活。
  • context: 用在制定工作進程啟動時的上下文,一般使用Pool() 或者一個context對象的Pool()方法來創(chuàng)建一個池,兩種方法都適當的設置了context。

實例方法:

  • apply(func[, args[, kwargs]]):在一個池工作進程中執(zhí)行func(args,*kwargs),然后返回結果。需要強調的是:此操作并不會在所有池工作進程中并執(zhí)行func函數。如果要通過不同參數并發(fā)地執(zhí)行func函數,必須從不同線程調用p.apply()函數或者使用p.apply_async()。它是阻塞的。apply很少使用
  •  apply_async(func[, arg[, kwds={}[, callback=None]]]):在一個池工作進程中執(zhí)行func(args,*kwargs),然后返回結果。此方法的結果是AsyncResult類的實例,callback是可調用對象,接收輸入參數。當func的結果變?yōu)榭捎脮r,將理解傳遞給callback。callback禁止執(zhí)行任何阻塞操作,否則將接收其他異步操作中的結果。它是非阻塞。
  • map(func, iterable[, chunksize=None]):Pool類中的map方法,與內置的map函數用法行為基本一致,它會使進程阻塞直到返回結果。注意,雖然第二個參數是一個迭代器,但在實際使用中,必須在整個隊列都就緒后,程序才會運行子進程。
  • map_async(func, iterable[, chunksize=None]):map_async與map的關系同apply與apply_async
  • imap():imap 與 map的區(qū)別是,map是當所有的進程都已經執(zhí)行完了,并將結果返回了,imap()則是立即返回一個iterable可迭代對象。
  • imap_unordered():不保證返回的結果順序與進程添加的順序一致。
  • close():關閉進程池,防止進一步操作。如果所有操作持續(xù)掛起,它們將在工作進程終止前完成。
  •  join():等待所有工作進程退出。此方法只能在close()或teminate()之后調用,讓其不再接受新的Process。
  • terminate():結束工作進程,不再處理未處理的任務。

方法apply_async()和map_async()的返回值是AsyncResul的實例obj。實例具有以下方法:

  • get():返回結果,如果有必要則等待結果到達。timeout是可選的。如果在指定時間內還沒有到達,將引發(fā)異常。如果遠程操作中引發(fā)了異常,它將在調用此方法時再次被引發(fā)。
  • ready():如果調用完成,返回True
  • successful():如果調用完成且沒有引發(fā)異常,返回True,如果在結果就緒之前調用此方法,引發(fā)異常
  • wait([timeout]):等待結果變?yōu)榭捎谩?
  • terminate():立即終止所有工作進程,同時不執(zhí)行任何清理或結束任何掛起工作。如果p被垃圾回收,將自動調用此函數
\# -*- coding:utf-8 -*-
Queue(用于進程通信,資源共享)
\# Pool+map
from multiprocessing import Pool
def test(i):
print(i)
if __name__ == "__main__":
lists = range(100)
pool = Pool(8)
pool.map(test, lists)
pool.close()
pool.join()
\# -*- coding:utf-8 -*-
\# 異步進程池(非阻塞)
from multiprocessing import Pool
def test(i):
print(i)
if __name__ == "__main__":
pool = Pool(8)
for i in range(100):
'''
For循環(huán)中執(zhí)行步驟:
(1)循環(huán)遍歷,將100個子進程添加到進程池(相對父進程會阻塞)
(2)每次執(zhí)行8個子進程,等一個子進程執(zhí)行完后,立馬啟動新的子進程。(相對父進程不阻塞)
apply_async為異步進程池寫法。異步指的是啟動子進程的過程,與父進程本身的執(zhí)行(print)是異步的,而For循環(huán)中往進程池添加子進程的過程,與父進程本身的執(zhí)行卻是同步的。
'''
pool.apply_async(test, args=(i,)) # 維持執(zhí)行的進程總數為8,當一個進程執(zhí)行完后啟動一個新進程.
print("test")
pool.close()
pool.join()
\# -*- coding:utf-8 -*-
\# 異步進程池(非阻塞)
from multiprocessing import Pool
def test(i):
print(i)
if __name__ == "__main__":
pool = Pool(8)
for i in range(100):
'''
實際測試發(fā)現,for循環(huán)內部執(zhí)行步驟:
(1)遍歷100個可迭代對象,往進程池放一個子進程
(2)執(zhí)行這個子進程,等子進程執(zhí)行完畢,再往進程池放一個子進程,再執(zhí)行。(同時只執(zhí)行一個子進程)
for循環(huán)執(zhí)行完畢,再執(zhí)行print函數。
'''
pool.apply(test, args=(i,)) # 維持執(zhí)行的進程總數為8,當一個進程執(zhí)行完后啟動一個新進程.
print("test")
pool.close()
pool.join()

Queue(用于進程通信,資源共享)

在使用多進程的過程中,最好不要使用共享資源。普通的全局變量是不能被子進程所共享的,只有通過Multiprocessing組件構造的數據結構可以被共享。

Queue是用來創(chuàng)建進程間資源共享的隊列的類,使用Queue可以達到多進程間數據傳遞的功能(缺點:只適用Process類,不能在Pool進程池中使用)。

構造方法:Queue([maxsize])

  • maxsize是隊列中允許最大項數,省略則無大小限制。

實例方法:

  • put():用以插入數據到隊列。put方法還有兩個可選參數:blocked和timeout。如果blocked為True(默認值),并且timeout為正值,該方法會阻塞timeout指定的時間,直到該隊列有剩余的空間。如果超時,會拋出Queue.Full異常。如果blocked為False,但該Queue已滿,會立即拋出Queue.Full異常。
  • get():可以從隊列讀取并且刪除一個元素。get方法有兩個可選參數:blocked和timeout。如果blocked為True(默認值),并且timeout為正值,那么在等待時間內沒有取到任何元素,會拋出Queue.Empty異常。如果blocked為False,有兩種情況存在,如果Queue有一個值可用,則立即返回該值,否則,如果隊列為空,則立即拋出Queue.Empty異常。若不希望在empty的時候拋出異常,令blocked為True或者參數全部置空即可。
  • get_nowait():同q.get(False)
  • put_nowait():同q.put(False)
  • empty():調用此方法時q為空則返回True,該結果不可靠,比如在返回True的過程中,如果隊列中又加入了項目。
  • full():調用此方法時q已滿則返回True,該結果不可靠,比如在返回True的過程中,如果隊列中的項目被取走。
  • qsize():返回隊列中目前項目的正確數量,結果也不可靠,理由同q.empty()和q.full()一樣

使用示例:

from multiprocessing import Process, Queue
import os, time, random
def write(q):
print('Process to write: %s' % os.getpid())
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random())
def read(q):
print('Process to read: %s' % os.getpid())
while True:
value = q.get(True)
print('Get %s from queue.' % value)
if __name__ == "__main__":
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
pw.start()
pr.start()
pw.join() # 等待pw結束
pr.terminate() # pr進程里是死循環(huán),無法等待其結束,只能強行終止

JoinableQueue就像是一個Queue對象,但隊列允許項目的使用者通知生成者項目已經被成功處理。通知進程是使用共享的信號和條件變量來實現的。

構造方法:JoinableQueue([maxsize])

  • maxsize:隊列中允許最大項數,省略則無大小限制。

實例方法

JoinableQueue的實例p除了與Queue對象相同的方法之外還具有:

  • task_done():使用者使用此方法發(fā)出信號,表示q.get()的返回項目已經被處理。如果調用此方法的次數大于從隊列中刪除項目的數量,將引發(fā)ValueError異常
  • join():生產者調用此方法進行阻塞,直到隊列中所有的項目均被處理。阻塞將持續(xù)到隊列中的每個項目均調用q.task_done()方法為止

使用示例:

\# -*- coding:utf-8 -*-
from multiprocessing import Process, JoinableQueue
import time, random
def consumer(q):
while True:
res = q.get()
print('消費者拿到了 %s' % res)
q.task_done()
def producer(seq, q):
for item in seq:
time.sleep(random.randrange(1,2))
q.put(item)
print('生產者做好了 %s' % item)
q.join()
if __name__ == "__main__":
q = JoinableQueue()
seq = ('產品%s' % i for i in range(5))
p = Process(target=consumer, args=(q,))
p.daemon = True # 設置為守護進程,在主線程停止時p也停止,但是不用擔心,producer內調用q.join保證了consumer已經處理完隊列中的所有元素
p.start()
producer(seq, q)
print('主線程')

Value,Array(用于進程通信,資源共享)

multiprocessing 中Value和Array的實現原理都是在共享內存中創(chuàng)建ctypes()對象來達到共享數據的目的,兩者實現方法大同小異,只是選用不同的ctypes數據類型而已。

Value

構造方法:Value((typecode_or_type, args[, lock])

  • typecode_or_type:定義ctypes()對象的類型,可以傳Type code或 C Type,具體對照表見下文。
  • args:傳遞給typecode_or_type構造函數的參數
  • lock:默認為True,創(chuàng)建一個互斥鎖來限制對Value對象的訪問,如果傳入一個鎖,如Lock或RLock的實例,將用于同步。如果傳入False,Value的實例就不會被鎖保護,它將不是進程安全的。

typecode_or_type支持的類型:

| Type code | C Type             | Python Type       | Minimum size in bytes |
| --------- | ------------------ | ----------------- | --------------------- |
| `'b'` | signed char | int | 1 |
| `'B'` | unsigned char | int | 1 |
| `'u'` | Py_UNICODE | Unicode character | 2 |
| `'h'` | signed short | int | 2 |
| `'H'` | unsigned short | int | 2 |
| `'i'` | signed int | int | 2 |
| `'I'` | unsigned int | int | 2 |
| `'l'` | signed long | int | 4 |
| `'L'` | unsigned long | int | 4 |
| `'q'` | signed long long | int | 8 |
| `'Q'` | unsigned long long | int | 8 |
| `'f'` | float | float | 4 |
| `'d'` | double | float | 8 |

參考地址:https://docs.python.org/3/library/array.html

Array

構造方法:Array(typecode_or_type, size_or_initializer, **kwds[, lock])

  • typecode_or_type:同上
  •  size_or_initializer:如果它是一個整數,那么它確定數組的長度,并且數組將被初始化為零。否則,size_or_initializer是用于初始化數組的序列,其長度決定數組的長度。
  • kwds:傳遞給typecode_or_type構造函數的參數
  • lock:同上

使用示例:

import multiprocessing
def f(n, a):
n.value = 3.14
a[0] = 5
if __name__ == '__main__':
num = multiprocessing.Value('d', 0.0)
arr = multiprocessing.Array('i', range(10))
p = multiprocessing.Process(target=f, args=(num, arr))
p.start()
p.join()
print(num.value)
print(arr[:])

注意:Value和Array只適用于Process類。

Pipe(用于管道通信)

多進程還有一種數據傳遞方式叫管道原理和 Queue相同。Pipe可以在進程之間創(chuàng)建一條管道,并返回元組(conn1,conn2),其中conn1,conn2表示管道兩端的連接對象,強調一點:必須在產生Process對象之前產生管道。

構造方法:Pipe([duplex])

  • dumplex:默認管道是全雙工的,如果將duplex射成False,conn1只能用于接收,conn2只能用于發(fā)送。

實例方法: