1. 程式人生 > >並發編程之多進程知識

並發編程之多進程知識

兩個 ive 查看進程 pipe 知識 將不 中一 count 守護


開啟進程的兩種方式
一 、multiprocessing模塊介紹
python中的多線程無法利用多核優勢,如果想要充分地使用多核CPU的資源(os.cpu_count()查看),在python中大部分情況需要使用多進程。Python提供了multiprocessing。
multiprocessing模塊用來開啟子進程,並在子進程中執行我們定制的任務(比如函數),該模塊與多線程模塊threading的編程接口類似。
  multiprocessing模塊的功能眾多:支持子進程、通信和共享數據、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。
需要再次強調的一點是:與線程不同,進程沒有任何共享狀態,進程修改的數據,改動僅限於該進程內。
二、 Process類的介紹

創建進程的類:
Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化得到的對象,
可用來開啟一個子進程
強調:
1. 需要使用關鍵字的方式來指定參數
2. args指定的為傳給target函數的位置參數,是一個元組形式,必須有逗號

參數介紹:
group參數未使用,值始終為None
target表示調用對象,即子進程要執行的任務
args表示調用對象的位置參數元組,args=(1,2,‘egon‘,)
kwargs表示調用對象的字典,kwargs={‘name‘:‘egon‘,‘age‘:18}
name為子進程的名稱

方法介紹:
p.start():啟動進程,並調用該子進程中的p.run()
p.run():進程啟動時運行的方法,正是它去調用target指定的函數,我們自定義類的類中一定要實現該方法
p.terminate():強制終止進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵屍進程,
使用該方法需要特別小心這種情況。如果p還保存了一個鎖那麽也將不會被釋放,進而導致死鎖
p.is_alive():如果p仍然運行,返回True
p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。
timeout是可選的超時時間。

屬性介紹:
p.daemon:默認值為False,如果設為True,代表p為後臺運行的守護進程,當p的父進程
終止時,p也隨之終止,並且設定為True後,p不能創建自己的新進程,必須在p.start()之前設置
p.name:進程的名稱
p.pid:進程的pid
三、Process類的使用
註意:在windows中Process()必須放到# if __name__ == ‘__main__‘:下
Since Windows has no fork, the multiprocessing module starts a new Python
process and imports the calling module.
If Process() gets called upon import, then this sets off an infinite succession of
new processes (or until your machine runs out of resources).
This is the reason for hiding calls to Process() inside

if __name__ == "__main__"
since statements inside this if-statement will not get called upon import.
由於Windows沒有fork,多處理模塊啟動一個新的Python進程並導入調用模塊。
如果在導入時調用Process(),那麽這將啟動無限繼承的新進程(或直到機器耗盡資源)。
這是隱藏對Process()內部調用的原,使用if __name__ == “__main __”,這個if語句
中的語句將不會在導入時被調用。


創建並開啟子進程的方式一
from multiprocessing import Process
import time


def task(name):
print(‘%s is running‘ % name)
time.sleep(3)
print(‘%s is done‘ % name)


if __name__ == ‘__main__‘:
# 實例化得到四個對象
p = Process(target=task, args=(‘子線程1‘,))
p2 = Process(target=task, args=(‘子線程2‘,))
p3 = Process(target=task, args=(‘子線程3‘,))
p4 = Process(target=task, args=(‘子線程4‘,))
# 調用對象下的方法,開啟四個進程
p.start() # 僅僅是給操作系統發送一個信號
p2.start()
p3.start()
p4.start()
print(‘主線程‘)
創建並開啟子進程的方式二
from multiprocessing import Process
import time


class MyProcess(Process):
def __init__(self,name):
super().__init__()
self.name = name

def run(self):
print(‘%s is running‘ % self.name)
time.sleep(3)
print(‘%s is done‘ % self.name)


if __name__ == ‘__main__‘:
# 實例化得到四個對象
p1 = MyProcess(‘子進程1‘)
p2 = MyProcess(‘子進程2‘)
p3 = MyProcess(‘子進程3‘)
p4 = MyProcess(‘子進程4‘)

p1.start() # start會自動調用run
p2.start()
p3.start()
p4.start()
print(‘主進程‘)

進程之間的內存空間是隔離的
from multiprocessing import Process
n = 100 # 在windows系統中應該把全局變量定義在if __name__ == ‘__main__‘之上就可以了


def work():
global n
print(‘子進程內部:‘, n)


if __name__ == ‘__main__‘:
p = Process(target=work)
p.start()
print(‘主進程內部:‘, n)


查看進程與pid
from multiprocessing import Process
import os
import time


def task():
print(‘%s is running,parent id is %s‘ % (os.getpid(), os.getppid()))
time.sleep(3)
print(‘%s is done,parent id is %s‘ % (os.getpid(), os.getppid()))


if __name__ == ‘__main__‘:
p = Process(target=task,)
p.start()
print(‘主進程‘, os.getpid(), os.getppid())

四、join方法
在主進程運行過程中如果想要並發的執行其他任務,我們可以開啟子進程,此時主進程的任務和子進程的任務分為兩種情況

  一種情況是:在主進程的任務與子進程的任務彼此獨立的情況下,主進程的任務先執行完畢後,主進程還需要等待子進程執行完畢,然後統一回收資源

  一種情況是:如果主進程的任務在執行到某一個階段時,需要等待子進程執行完畢後才能繼續執行,就需要一種機制能夠讓主進程監測子進程是否運行完畢,在子進程執行完畢後才繼續執行,否則一直在原地阻塞,這就是join方法的作用。

from multiprocessing import Process
import time
import os


def task():
print(‘%s is running,parent id is <%s>‘ % (os.getpid(), os.getppid()))
time.sleep(3)
print(‘%s is done,parent id is <%s>‘ % (os.getpid(), os.getppid()))


if __name__ == ‘__main__‘:
p = Process(target=task,)
p.start()

p.join()
print(‘主進程‘, os.getpid(), os.getppid())
print(p.pid)

join() 等待進程結束,是讓主線程等待;
多任務多並發時,一個進程結束,另一個進程可能早已結束;理論上,jion花費的總時間是耗時間最長的那個進程運行的時間

五、Process對象的其他屬性或方法
進程對象的其他方法一:terminate(關閉進程,不會立即關閉)與is_alive(查看進程是否存活,返回的是布爾類型)
from multiprocessing import Process
import time
import os


def task():
print(‘%s is running,parent id is <%s>‘ %(os.getpid(),os.getppid()))
time.sleep(3)
print(‘%s is done,parent id is <%s>‘ %(os.getpid(),os.getppid()))


def task1(name):
print(‘%s is running‘ % name)
time.sleep(3)
print(‘%s is done‘ % name)


if __name__ == ‘__main__‘:
p1 = Process(target=task1, args=(‘egon‘,), name=‘子進程1‘) # 可以用關鍵參數來指定進程名
p=Process(target=task,name=‘sub——Precsss‘)
p.start()
p1.start()
p.terminate() # 關閉進程,不會立即關閉,所以is_alive立刻查看的結果可能還是存活
time.sleep(1)
print(p.is_alive()) # 結果為False
print(‘主進程‘)
print(p.name)
print(p1.name, p1.pid,)


六、守護進程
主進程創建子進程,然後將該進程設置成守護自己的進程
特點:1.守護進程會在主進程代碼執行結束後就終止
2.守護進程內無法再開啟子進程,否則拋出異常:AssertionError: daemonic processes
are not allowed to have children

如果我們有兩個任務需要並發執行,可以開一個主進程和一個子線程,子線程應該在開啟之前設置成守護進程,這樣主程序運行結束,守護進程也終止了

from multiprocessing import Process
import time
import random


def task(name):
print(‘%s is running‘ % name)
time.sleep(random.randrange(1, 3))
print(‘%s is run done‘ % name)


if __name__ == ‘__main__‘:
p = Process(target=task, args=(‘egon‘,))
p.daemon = True # 一定要在p.start()前設置,設置p為守護進程,禁止p創建子進程,並且父進程代碼執行結束,p即終止運行
p.start()
print(‘主‘) # 只要終端打印出這一行內容,那麽守護進程p也就跟著結束掉了

七、基於多進程實現的並發套接字通信

八、互斥鎖

九、隊列

十、生產者消費者模型

並發編程之多進程知識