Python3學習筆記11-標準庫之子進程、信號、多線程、進程信息

關於進程與線程的簡單理解(以工廠舉例:cup-》工廠,車間-》進程,線程-》工人),能夠參考阮一峯的博文進程與線程的一個簡單圖文解釋html

1、子進程 (subprocess包)

這裏的內容以Linux進程基礎和Linux文本流爲基礎。subprocess包主要功能是執行外部的命令和程序。好比說,我須要使用wget下載文件。我在Python中調用wget程序。從這個意義上來講,subprocess的功能與shell相似。python

一、subprocess以及經常使用的封裝函數

當咱們運行python的時候,咱們都是在建立並運行一個進程一個進程能夠fork一個子進程,並讓這個子進程exec另一個程序。在Python中,咱們經過標準庫中的subprocess包fork一個子進程,並運行一個外部的程序linux

subprocess包中定義有數個建立子進程的函數,這些函數分別以不一樣的方式建立子進程,因此咱們能夠根據須要來從中選取一個使用。另外subprocess還提供了一些管理**標準流(standard stream)管道(pipe)**的工具,從而在進程間使用文本通訊。shell

使用subprocess包中的函數建立子進程的時候,要注意:編程

  1. 在建立子進程以後,父進程是否暫停,並等待子進程運行。windows

  2. 函數返回什麼緩存

  3. 當returncode不爲0時,父進程如何處理。安全

###(1)、subprocess.call()服務器

父進程等待子進程完成網絡

返回退出信息(returncode,至關於exit code)

###(2)、subprocess.check_call()

父進程等待子進程完成

返回0

檢查退出信息,若是returncode不爲0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性,可用try...except...來檢查(見Python錯誤處理)。

###(3)、subprocess.check_output()

父進程等待子進程完成

返回子進程向標準輸出的輸出結果

檢查退出信息,若是returncode不爲0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性和output屬性,output屬性爲標準輸出的輸出結果,可用try...except...來檢查。

這三個函數的使用方法相相似,咱們以subprocess.call()來講明:

import subprocess
rc = subprocess.call(["ls","-l"])

輸入圖片說明 咱們將程序名(ls)和所帶的參數(-l)一塊兒放在一個中傳遞給subprocess.call()
就等於在Linux系統下,使用命令對系統的shell進行操做:** ls -l 列出當前路徑下的文件**

輸入圖片說明

能夠經過一個shell來解釋一整個字符串:

import subprocess
out = subprocess.call("ls -l", shell=True)
out = subprocess.call("cd ..", shell=True)

咱們使用了shell=True這個參數。這個時候,咱們使用一整個字符串,而不是一個表來運行子進程。Python將先運行一個shell,再用這個shell來解釋這整個字符串

shell命令中有一些是shell的內建命令,這些命令必須經過shell運行,$cd。shell=True容許咱們運行這樣一些命令。

###二、Popen() 實際上,咱們上面的三個函數都是基於Popen()封裝(wrapper)。這些封裝的目的在於讓咱們容易使用子進程。當咱們想要更個性化咱們的需求的時候,就要轉向Popen類該類生成的對象用來表明子進程

與上面的封裝不一樣,Popen對象建立後,主程序不會自動等待子進程完成。咱們必須調用對象的wait()方法,父進程纔會等待 (也就是阻塞block)

import subprocess
child = subprocess.Popen(["ping","-c","5","www.google.com"])
print("parent process")

子進程 從運行結果中看到,父進程在開啓子進程以後並無等待child的完成,而是直接運行print。 在運行子進程時,父進程也在運行

對比等待的狀況:

import subprocess
child = subprocess.Popen(["ping","-c","5","www.google.com"])
child.wait()
print("parent process")

輸入圖片說明 此外,你還能夠在父進程中對子進程進行其它操做,好比咱們上面例子中的child對象:

child.poll() # 檢查子進程狀態

child.kill() # 終止子進程

child.send_signal() # 向子進程發送信號

child.terminate() # 終止子進程

子進程的PID存儲在child.pid

###三、子進程的文本流控制 (沿用child子進程) 子進程的標準輸入,標準輸出和標準錯誤也能夠經過以下屬性表示:

child.stdin
child.stdout
child.stderr

咱們能夠在Popen()創建子進程的時候改變標準輸入、標準輸出和標準錯誤,並能夠利用subprocess.PIPE將多個子進程的輸入和輸出鏈接在一塊兒,構成管道(pipe):

import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)

輸入圖片說明
subprocess.PIPE實際上爲文本流提供一個緩存區。child1的stdout將文本輸出到緩存區,隨後child2的stdin從該PIPE中將文本讀取走。child2的輸出文本也被存放在PIPE中,直到**communicate()**方法從PIPE中讀取出PIPE中的文本。

要注意的是,communicate()是Popen對象的一個方法,該方法會阻塞父進程,直到子進程完成

咱們還能夠利用communicate()方法來使用PIPE給子進程輸入:

import subprocess
child = subprocess.Popen(["cat"], stdin=subprocess.PIPE)
child.communicate("vamei")

輸入圖片說明
咱們啓動子進程以後,cat會等待輸入,直到咱們用communicate()輸入"vamei"。

經過使用subprocess包,咱們能夠運行外部程序。這極大的拓展了Python的功能。若是你已經瞭解了操做系統的某些應用,你能夠從Python中直接調用該應用(而不是徹底依賴Python),並將應用的結果輸出給Python,並讓Python繼續處理。shell的功能(好比利用文本流鏈接各個應用),就能夠在Python中實現。

##2、信號 (signal包) signal包負責在Python程序內部處理信號,典型的操做包括預設信號處理函數,暫停並等待信號,以及定時發出SIGALRM等。要注意,signal包主要是針對UNIX平臺(好比Linux, MAC OS),而Windows內核中因爲對信號機制的支持不充分,因此在Windows上的Python不能發揮信號系統的功能。

###一、定義信號名 signal包定義了各個信號名及其對應的整數,好比:

import signal
print signal.SIGALRM
print signal.SIGCONT

輸入圖片說明 Python所用的信號名和Linux一致。你能夠經過如下命令查詢:

$man 7 signal

###二、預設信號處理函數 signal包的核心是使用**signal.signal()函數來預設(register)**信號處理函數,以下所示:

singnal.signal(signalnum, handler)

signalnum爲某個信號,handler爲該信號的處理函數。咱們在信號基礎裏提到,進程能夠無視信號,能夠採起默認操做,還能夠自定義操做。當handler爲signal.SIG_IGN時,信號被無視(ignore)。當handler爲singal.SIG_DFL,進程採起默認操做(default)。當handler爲一個函數名時,進程採起函數中定義的操做。

import signal
# Define signal handler function
def myHandler(signum, frame):
    print('I received: ', signum)

# register signal.SIGTSTP's handler 
signal.signal(signal.SIGTSTP, myHandler)
signal.pause()
print('End of Signal Demo')

在主程序中,咱們首先使用signal.signal()函數來預設信號處理函數。而後咱們執行signal.pause()來讓該進程暫停以等待信號,以等待信號。當信號SIGUSR1被傳遞給該進程時,進程從暫停中恢復,並根據預設,執行SIGTSTP的信號處理函數myHandler()。myHandler的兩個參數一個用來識別信號(signum),另外一個用來得到信號發生時,進程棧的情況(stack frame)。這兩個參數都是由signal.singnal()函數來傳遞的。

上面的程序能夠保存在一個文件中(好比test.py)。咱們使用以下方法運行: ** $python test.py **

輸入圖片說明
以便讓進程運行。當程序運行到signal.pause()的時候,進程暫停並等待信號。此時,經過按下CTRL+Z向該進程發送SIGTSTP信號。咱們能夠看到,進程執行了myHandle()函數, 隨後返回主程序,繼續執行。(固然,也能夠用$ps查詢process ID, 再使用$kill來發出信號。)

(進程並不必定要使用signal.pause()暫停以等待信號,它也能夠在進行工做中接受信號,好比將上面的signal.pause()改成一個須要長時間工做的循環。) 咱們能夠根據本身的須要更改myHandler()中的操做,以針對不一樣的信號實現個性化的處理。

###三、定時發出SIGALRM信號 一個有用的函數是signal.alarm(),它被用於在必定時間以後,向進程自身發送SIGALRM信號:

import signal
# Define signal handler function
def myHandler(signum, frame):
    print("Now, it's the time")
    exit()

# register signal.SIGALRM's handler 
signal.signal(signal.SIGALRM, myHandler)
signal.alarm(5)
while True:
    print('not yet')

咱們這裏用了一個無限循環以便讓進程持續運行。在signal.alarm()執行5秒以後,進程將向本身發出SIGALRM信號,隨後,信號處理函數myHandler開始執行。

###四、發送信號 signal包的核心是設置信號處理函數。除了**signal.alarm()**向自身發送信號以外,並無其餘發送信號的功能。但在os包中,有相似於linux的kill命令的函數,分別爲:

os.kill(pid, sid)

os.killpg(pgid, sid)

分別向進程和進程組發送信號。sid爲信號所對應的整數或者singal.SIG*。

實際上signal, pause,kill和alarm都是Linux應用編程中常見的C庫函數,在這裏,咱們只不過是用Python語言來實現了一下。實際上,Python 的解釋器是使用C語言來編寫的,因此有此類似性也並不意外。此外,在Python 3.4中,signal包被加強,信號阻塞等功能被加入到該包中。咱們暫時不深刻到該包中。

##3、多線程與同步 (threading包) Python主要經過標準庫中的threading包來實現多線程。在當今網絡時代,每一個服務器都會接收到大量的請求。服務器能夠利用多線程的方式來處理這些請求,以提升對網絡端口的讀寫效率。Python是一種網絡服務器的後臺工做語言,因此多線程也就很天然被Python語言支持。

進程與線程的簡單理解(以工廠舉例:cup-》工廠,車間-》進程,線程-》工人),,能夠參考該博文http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

(關於多線程的原理和C實現方法,請參考Linux多線程與同步,要了解race condition, mutex和condition variable的概念) ###一、多線程售票以及同步 咱們使用Python來實現Linux多線程與同步文中的售票程序。咱們使用mutex即"互斥鎖"(Mutual exclusion,縮寫 Mutex), (也就是Python中的Lock類對象) 來實現線程的同步:

# A program to simulate selling tickets in multi-thread way
# Written by Vamei

import threading
import time
import os

# This function could be any function to do other chores.
def doChore():
    time.sleep(0.5)

# Function for each thread
def booth(tid):
    global i
    global lock
    while True:
        lock.acquire()                # Lock; or wait if other thread is holding the lock
        if i != 0:
            i = i - 1                 # Sell tickets
            print(tid,':now left:',i) # Tickets left
            doChore()                 # Other critical operations
        else:
            print("Thread_id",tid," No more tickets")
            os._exit(0)              # Exit the whole process immediately
        lock.release()               # Unblock
        doChore()                    # Non-critical operations

# Start of the main function
i    = 100                           # Available ticket number 
lock = threading.Lock()              # Lock (i.e., mutex)

# Start 10 threads
for k in range(10):
    new_thread = threading.Thread(target=booth,args=(k,))   # Set up thread; target: the callable (function) to be run, args: the argument for the callable 
    new_thread.start()                                      # run the thread

輸入圖片說明

咱們使用了兩個全局變量,一個是i,用以儲存剩餘票數;一個是lock對象,用於同步線程對i的修改。此外,在最後的for循環中,咱們總共設置了10個線程。每一個線程都執行booth()函數。線程在調用start()方法的時候正式啓動 (實際上,計算機中最多會有11個線程,由於主程序自己也會佔用一個線程)。Python使用threading.Thread對象來表明線程,用threading.Lock對象來表明一個互斥鎖 (mutex)

有兩點須要注意:

咱們在函數中使用global來聲明變量爲全局變量,從而讓多線程共享i和lock (在C語言中,咱們經過將變量放在全部函數外面來讓它成爲全局變量)。若是不這麼聲明,因爲i和lock是不可變數據對象,它們將被看成一個局部變量。若是是可變數據對象的話,則不須要global聲明。咱們甚至能夠將可變數據對象做爲參數來傳遞給線程函數。這些線程將共享這些可變數據對象。

咱們在booth中使用了兩個doChore()函數。能夠在將來改進程序,以便讓線程除了進行i=i-1以外,作更多的操做,好比打印剩餘票數,找錢,或者喝口水之類的。第一個doChore()依然在Lock內部,因此能夠安全地使用共享資源 (critical operations, 好比打印剩餘票數)。第二個doChore()時,Lock已經被釋放,因此不能再去使用共享資源。這時候能夠作一些不使用共享資源的操做 (non-critical operation, 好比找錢、喝水)。我故意讓doChore()等待了0.5秒,以表明這些額外的操做可能花費的時間。你能夠定義的函數來代替doChore()。

###二、OOP建立線程 上面的Python程序很是相似於一個面向過程的C程序。咱們下面介紹如何經過面向對象 (OOP, object-oriented programming) 的方法實現多線程,其核心是繼承threading.Thread類。咱們上面的for循環中已經利用了threading.Thread()的方法來建立一個Thread對象,並將函數booth()以及其參數傳遞給改對象,並調用start()方法來運行線程。OOP的話,經過修改Thread類的**run()**方法來定義線程所要執行的命令。

# A program to simulate selling tickets in multi-thread way
# Written by Vamei

import threading
import time
import os

# This function could be any function to do other chores.
def doChore():
    time.sleep(0.5)

# Function for each thread
class BoothThread(threading.Thread):
    def __init__(self, tid, monitor):
        self.tid          = tid
        self.monitor = monitor
        threading.Thread.__init__(self)
    def run(self):
        while True:
            monitor['lock'].acquire()                          # Lock; or wait if other thread is holding the lock
            if monitor['tick'] != 0:
                monitor['tick'] = monitor['tick'] - 1          # Sell tickets
                print(self.tid,':now left:',monitor['tick'])   # Tickets left
                doChore()                                      # Other critical operations
            else:
                print("Thread_id",self.tid," No more tickets")
                os._exit(0)                                    # Exit the whole process immediately
            monitor['lock'].release()                          # Unblock
            doChore()                                          # Non-critical operations

# Start of the main function
monitor = {'tick':100, 'lock':threading.Lock()}

# Start 10 threads
for k in range(10):
    new_thread = BoothThread(k, monitor)
    new_thread.start()

咱們本身定義了一個類BoothThread, 這個類繼承自thread.Threading類。而後咱們把上面的booth()所進行的操做通通放入到BoothThread類的run()方法中。注意,咱們沒有使用全局變量聲明global,而是使用了一個詞典 monitor存放全局變量,而後把詞典做爲參數傳遞給線程函數。因爲詞典是可變數據對象,因此當它被傳遞給函數的時候,函數所使用的依然是同一個對象,至關於被多個線程所共享。這也是多線程乃至於多進程編程的一個技巧 (應儘可能避免上面的global聲明的用法,由於它並不適用於windows平臺)。

上面OOP編程方法與面向過程的編程方法相比,並無帶來太大實質性的差異。

###三、其餘 threading.Thread對象: 咱們已經介紹了該對象的start()和run(), 此外:

join()方法,調用該方法的線程將等待直到改Thread對象完成,再恢復運行。這與進程間調用wait()函數相相似。 下面的對象用於處理多線程同步。對象一旦被創建,能夠被多個線程共享,並根據狀況阻塞某些進程。

threading.Lock對象: mutex, 有acquire()和release()方法。

threading.Condition對象: condition variable,創建該對象時,會包含一個Lock對象 (由於condition variable老是和mutex一塊兒使用)。能夠對Condition對象調用acquire()和release()方法,以控制潛在的Lock對象。此外:

  • wait()方法,至關於cond_wait()

  • notify_all(),至關與cond_broadcast()

  • nofify(),與notify_all()功能相似,但只喚醒一個等待的線程,而不是所有

threading.Semaphore對象: semaphore,也就是計數鎖(semaphore傳統意義上是一種進程間同步工具)。建立對象的時候,能夠傳遞一個整數做爲計數上限 (sema = threading.Semaphore(5))。它與Lock相似,也有Lock的兩個方法。

threading.Event對象: 與threading.Condition相相似,至關於沒有潛在的Lock保護的condition variable。對象有True和False兩個狀態。能夠多個線程使用wait()等待,直到某個線程調用該對象的set()方法,將對象設置爲True。線程能夠調用對象的clear()方法來重置對象爲False狀態。

##4、進程信息 (部分os包) Python的os包中有查詢和修改進程信息的函數。學習Python的這些工具也有助於理解Linux體系。

###一、進程信息 os包中相關函數以下:

(1)、uname() 返回操做系統相關信息。相似於Linux上的uname命令。

(2)、umask() 設置該進程建立文件時的權限mask。相似於Linux上的umask命令

(3)、get() 查詢 (由如下代替)

uid, euid, resuid, gid, egid, resgid :權限相關,其中resuid主要用來返回saved UID

pid, pgid, ppid, sid                 :進程相關

(4)、put*() 設置 (*由如下代替)

euid, egid: 用於更改euid,egid。

uid, gid  : 改變進程的uid, gid。只有super user纔有權改變進程uid和gid (意味着要以$sudo python的方式運行Python)。

pgid, sid : 改變進程所在的進程組(process group)和會話(session)。

(5)、getenviron():得到進程的環境變量

(6)、setenviron():更改進程的環境變量

例1,進程的real UID和real GID:

import os
print(os.getuid())
print(os.getgid())

將上面的程序保存爲py_id.py文件,分別用$python py_id.py和$sudo python py_id.py看一下運行結果。

###二、saved UID和saved GID 咱們但願saved UID和saved GID如咱們在Linux用戶與「最小權限」原則中描述的那樣工做,但這很難。緣由在於,當咱們寫一個Python腳本後,咱們實際運行的是python這個解釋器,而不是Python腳本文件。對比C,C語言直接運行由C語言編譯成的執行文件。咱們必須更改python解釋器自己的權限來運用saved UID機制,然而這麼作又是異常危險的。

好比說,咱們的python執行文件爲/usr/bin/python (你能夠經過$which python獲知)

咱們先看一下:

$ls -l /usr/bin/python

的結果:

-rwxr-xr-x root root
咱們修改權限以設置set UID和set GID位 (參考Linux用戶與「最小權限」原則):

$sudo chmod 6755 /usr/bin/python

/usr/bin/python的權限成爲:

-rwsr-sr-x root root

隨後,咱們運行文件下面test.py文件,這個文件能夠是由普通用戶全部:

import os
print(os.getresuid())

咱們獲得結果:(1000, 0, 0) 上面分別是UID,EUID,saved UID。咱們只用執行一個由普通用戶擁有的python腳本,就能夠獲得super user的權限!因此,這樣作是極度危險的,咱們至關於交出了系統的保護系統。想像一下Python強大的功能,別人如今能夠用這些強大的功能做爲攻擊你的武器了!使用下面命令來恢復到從前:

$sudo chmod 0755 /usr/bin/python

關於腳本文件的saved UID/GID,更加詳細的討論見:http://www.faqs.org/faqs/unix-faq/faq/part4/section-7.html

##5、多進程初步 (multiprocessing包) 咱們已經見過了使用subprocess包來建立子進程,但這個包有兩個很大的侷限性:

(1) 咱們老是讓subprocess運行外部的程序,而不是運行一個Python腳本內部編寫的函數。

(2) 進程間只經過管道進行文本交流。以上限制了咱們將subprocess包應用到更普遍的多進程任務。(這樣的比較實際是不公平的,由於subprocessing自己就是設計成爲一個shell,而不是一個多進程管理包)。

###一、threading和multiprocessing
multiprocessing包是Python中的多進程管理包。與threading.Thread相似,它能夠利用multiprocessing.Process對象來建立一個進程。該進程能夠運行在Python程序內部編寫的函數。該Process對象與Thread對象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition類 (這些對象能夠像多線程那樣,經過參數傳遞給各個進程),用以同步進程,其用法與threading包中的同名類一致。因此,multiprocessing的很大一部份與threading使用同一套API,只不過換到了多進程的情境。

但在使用這些共享API的時候,咱們要注意如下幾點:

在UNIX平臺上,當某個進程終結以後,該進程須要被其父進程調用wait,不然進程成爲殭屍進程(Zombie)。因此,有必要對每一個Process對象調用join()方法 (實際上等同於wait)。對於多線程來講,因爲只有一個進程,因此不存在此必要性。

multiprocessing提供了threading包中沒有的IPC(好比Pipe和Queue),效率上更高。應優先考慮Pipe和Queue,避免使用 Lock/Event/Semaphore/Condition等同步方式 (由於它們佔據的不是用戶進程的資源)。

多進程應該避免共享資源。在多線程中,咱們能夠比較容易地共享資源,好比使用全局變量或者傳遞參數。在多進程狀況下,因爲每一個進程有本身獨立的內存空間,以上方法並不合適。此時咱們能夠經過共享內存和Manager的方法來共享資源。但這樣作提升了程序的複雜度,並由於同步的須要而下降了程序的效率。

Process.PID中保存有PID,若是進程尚未start(),則PID爲None

咱們能夠從下面的程序中看到Thread對象和Process對象在使用上的類似性與結果上的不一樣。各個線程和進程都作一件事:打印PID。但問題是,全部的任務在打印的時候都會向同一個標準輸出(stdout)輸出。這樣輸出的字符會混合在一塊兒,沒法閱讀。使用Lock同步在一個任務輸出完成以後,再容許另外一個任務輸出,能夠避免多個任務同時向終端輸出

# Similarity and difference of multi thread vs. multi process
# Written by Vamei

import os
import threading
import multiprocessing

# worker function
def worker(sign, lock):
    lock.acquire()
    print(sign, os.getpid())
    lock.release()

# Main
print('Main:',os.getpid())

# Multi-thread
record = []
lock  = threading.Lock()
for i in range(5):
    thread = threading.Thread(target=worker,args=('thread',lock))
    thread.start()
    record.append(thread)

for thread in record:
    thread.join()

# Multi-process
record = []
lock = multiprocessing.Lock()
for i in range(5):
    process = multiprocessing.Process(target=worker,args=('process',lock))
    process.start()
    record.append(process)

for process in record:
    process.join()

輸入圖片說明

運行結果打印:

輸入圖片說明

從打印的結果咱們能夠看出,全部Thread的PID都與主程序相同,而每一個Process都有一個不一樣的PID。

###二、Pipe和Queue 管道PIPE消息隊列message queue,multiprocessing包中有Pipe類和Queue類來分別支持這兩種IPC機制。Pipe和Queue能夠用來傳送常見的對象。

(1)、 Pipe能夠是單向(half-duplex),也能夠是雙向(duplex)。咱們經過mutiprocessing.Pipe(duplex=False)建立**單向管道 **(默認爲雙向)。一個進程從PIPE一端輸入對象,而後被PIPE另外一端的進程接收,單向管道只容許管道一端的進程輸入,而雙向管道則容許從兩端輸入

下面的程序展現了Pipe的使用:

# Multiprocessing with Pipe
# Written by Vamei

import multiprocessing as mul

def proc1(pipe):
    pipe.send('hello')
    print('proc1 rec:',pipe.recv())

def proc2(pipe):
    print('proc2 rec:',pipe.recv())
    pipe.send('hello, too')

# Build a pipe
pipe = mul.Pipe()

# Pass an end of the pipe to process 1
p1   = mul.Process(target=proc1, args=(pipe[0],))
# Pass the other end of the pipe to process 2
p2   = mul.Process(target=proc2, args=(pipe[1],))
p1.start()
p2.start()
p1.join()
p2.join()

輸入圖片說明
這裏的Pipe是雙向的。

Pipe對象創建的時候,返回一個含有兩個元素的表,每一個元素表明Pipe的一端(Connection對象)。咱們對Pipe的某一端調用**send()方法來傳送對象,在另外一端使用recv()**來接收。

(2)、 Queue與Pipe相相似,都是先進先出的結構。但Queue容許多個進程放入,多個進程從隊列取出對象。Queue使用**mutiprocessing.Queue(maxsize)**建立,maxsize表示隊列中能夠存放對象的最大數量。

下面的程序展現了Queue的使用:

# Written by Vamei
import os
import multiprocessing
import time
#==================
# input worker
def inputQ(queue):
    info = str(os.getpid()) + '(put):' + str(time.time())
    queue.put(info)

# output worker
def outputQ(queue,lock):
    info = queue.get()
    lock.acquire()
    print (str(os.getpid()) + '(get):' + info)
    lock.release()
#===================
# Main
record1 = []   # store input processes
record2 = []   # store output processes
lock  = multiprocessing.Lock()    # To prevent messy print
queue = multiprocessing.Queue(3)

# input processes
for i in range(10):
    process = multiprocessing.Process(target=inputQ,args=(queue,))
    process.start()
    record1.append(process)

# output processes
for i in range(10):
    process = multiprocessing.Process(target=outputQ,args=(queue,lock))
    process.start()
    record2.append(process)

for p in record1:
    p.join()

queue.close()  # No more object will come, close the queue

for p in record2:
    p.join()

輸入圖片說明

打印結果:

輸入圖片說明

一些進程使用put()在Queue中放入字符串,這個字符串中包含PID和時間。另外一些進程從Queue中取出,並打印本身的PID以及get()的字符串。

注:本Python學習筆記是按照Vamei的博客教程來學習的,若有興趣能夠參考Vamei Python快速開發博文

相關文章
相關標籤/搜索