一、多進(jìn)程概述
- 程序:是一個(gè)指令的集合
- 進(jìn)程:正在執(zhí)行的程序;或者說:當(dāng)你運(yùn)行一個(gè)程序,你就啟動(dòng)了一個(gè)進(jìn)程
- 編寫完的代碼,沒有運(yùn)行時(shí),稱為程序,正在運(yùn)行的代碼,稱為進(jìn)程;
- 程序是死的(靜態(tài)的),進(jìn)程是活的(動(dòng)態(tài)的)
- 操作系統(tǒng)輪流讓各個(gè)任務(wù)交替執(zhí)行,由于CPU的執(zhí)行速度實(shí)在是太快了,我們感覺就像所有任務(wù)都在同時(shí)執(zhí)行一樣;
- 多進(jìn)程中,每個(gè)進(jìn)程中所有數(shù)據(jù)(包括全局變量)都各自擁有一份,互相不影響。
- 程序啟動(dòng)運(yùn)行時(shí),首先會(huì)創(chuàng)建一個(gè)主進(jìn)程
- 在主進(jìn)程(父進(jìn)程)下,我們可以創(chuàng)建新的進(jìn)程(子進(jìn)程),子進(jìn)程依賴于主進(jìn)程,如果主進(jìn)程結(jié)束,程序會(huì)退出
- 創(chuàng)建子進(jìn)程時(shí)會(huì)在子進(jìn)和空間中復(fù)制一份主進(jìn)程的代碼,全局變量在多個(gè)子進(jìn)程之間不共享,進(jìn)程之間的數(shù)據(jù)是獨(dú)立的,默認(rèn)情況下互不影響。
二、通過調(diào)用multiprocessing.Process實(shí)現(xiàn)多進(jìn)程
- Python提供了非常好用的多進(jìn)程包multiprocessing,借助這個(gè)包,可以輕松完成從單進(jìn)程到并發(fā)執(zhí)行的轉(zhuǎn)換。
- multiprocessing模塊提供了一個(gè)Process類來創(chuàng)建一個(gè)進(jìn)程對(duì)象。
- 如果用循環(huán)創(chuàng)建多個(gè)進(jìn)程時(shí),可以把多個(gè)子進(jìn)程對(duì)象加到list中,在循環(huán)外再循環(huán)執(zhí)行l(wèi)ist中的每個(gè)對(duì)象的join()方法以達(dá)到主進(jìn)程等待所有子進(jìn)程都結(jié)束。
import timefrom multiprocessing import Process
def sing(name):
for i in range(10):
print(name+"唱歌")
time.sleep(1)
def dance():
for i in range(10):
print("跳舞")
time.sleep(1)
if __name__ == '__main__':
p1 = Process(target=sing, args=('林志凌',), name="sing")
p2 = Process(target=dance , name="dance")
p1.start()
print(p1.name)
p2.start()
print(p2.name)
p1.join() #join() 主進(jìn)程等待子進(jìn)程結(jié)束
p2.join()
- Process 類常用方法
- p.start():啟動(dòng)進(jìn)程,并調(diào)用該子進(jìn)程中的p.run()
- p.run():進(jìn)程啟動(dòng)時(shí)運(yùn)行的方法,正是它去調(diào)用target指定的函數(shù),我們自定義類的類中一定要實(shí)現(xiàn)該方法
- p.terminate() 強(qiáng)制終止進(jìn)程p,不會(huì)進(jìn)行任何清理操作
- p.is_alive() 如果p仍然運(yùn)行,返回True,用于判斷進(jìn)程是否在運(yùn)行
- p.join([timeout]) 主進(jìn)程等待子進(jìn)程結(jié)束,timeout是可選的超時(shí)時(shí)間。
- Process類常用屬性
- name:當(dāng)前進(jìn)程實(shí)例別名,默認(rèn)為Process-N,N從1開始遞增
- pid:當(dāng)前進(jìn)程實(shí)例的PID值
三、通過繼承Process類創(chuàng)建多進(jìn)程
import multiprocessing
import time
class ClockProcess(multiprocessing.Process):
def __init__(arg):
super().__init__()
self.arg = arg
def run(self):
n = self.arg
while n>0:
print(n)
time.sleep(1)
n -= 1
if __name__ == '__main__':
p = ClockProcess(5)
p.start()
p.join()
四、進(jìn)程池-Pool
- 進(jìn)程池:用來創(chuàng)建多個(gè)進(jìn)程
- 當(dāng)需要?jiǎng)?chuàng)建的子進(jìn)程數(shù)量不多時(shí),可以直接利用multiprocessing中的Process動(dòng)態(tài)生成多個(gè)進(jìn)程,但如果要?jiǎng)?chuàng)建的子進(jìn)程數(shù)很多時(shí),此時(shí)可以用到multiprocessing模塊提供的Pool
- 初始化Pool時(shí),可以指定一個(gè)最大進(jìn)程數(shù), 當(dāng)有新的請(qǐng)求提交到Pool中時(shí),如果進(jìn)程池還沒有滿,那么就會(huì)創(chuàng)建一個(gè)新的進(jìn)程用來執(zhí)行該請(qǐng)求;但如果進(jìn)程池中的進(jìn)程數(shù)已經(jīng)達(dá)到最大值,那么該請(qǐng)求就會(huì)等待,直到進(jìn)程池中有進(jìn)程結(jié)束,才會(huì)創(chuàng)建新的進(jìn)程來執(zhí)行。
from multiprocessing import Pool
import random, time
def work(num):
print(random.random() * num)
time.sleep(3)
if __name__ == '__main__':
po = Pool(4) #如果不寫默認(rèn)為當(dāng)前CPU核數(shù)
for i in range(10):
po.apply_async(work, (i,))
po.close()
po.join()
- multiprocessing.Pool常用函數(shù)解析:
- apply_async(self, func, args=(), kwds={}, callback=None, error_callback=None):使用非阻塞方式調(diào)用fun(并行執(zhí)行,阻塞方式必須等上一個(gè)進(jìn)程退出才能執(zhí)行下一個(gè)進(jìn)程)args為傳遞給func的參數(shù)列表,kwds為傳遞給func的關(guān)鍵字參數(shù)列表;
- apply(func[, args[, kwds]]) (了解即可幾乎不用)使用阻塞的方式調(diào)用func
- close():關(guān)閉Pool,使其不再接受新的任務(wù);
- terminate():不管任務(wù)是否完成,立即終止;
- join():主進(jìn)程阻塞,等待子進(jìn)程退出,必須在close()或terminate之后使用;
五、 進(jìn)程間通信-Queue
- 可以使用multiprocessing模塊的Queue實(shí)現(xiàn)多進(jìn)程間的數(shù)據(jù)傳遞
- 初始化Queue()對(duì)象時(shí)(例如:q = Queue()),若括號(hào)中沒有指定最大可接收的消息數(shù)量,或數(shù)量為負(fù)值,那么就代表可接受的消息數(shù)量沒有上限
- Queue.qsize():返回當(dāng)前隊(duì)列包含的消息數(shù)量
- Queue.empty():如果隊(duì)列為空,返回True,反之False
- Queue.full():如果隊(duì)列滿了,返回True,反之False
- Queue.get([block[, timeout]]):獲取隊(duì)列中的一條消息,然后將其從隊(duì)列中移除,block默認(rèn)值為True
- 如果block使用默認(rèn)值,且沒有設(shè)置timeout(單位移),消息隊(duì)列如果為空,此時(shí)程序?qū)⒈蛔枞ㄍT谧x取狀態(tài)),直到從消息隊(duì)列讀到消息為止,如果設(shè)置了timeout,則會(huì)等待timeout秒,若還沒讀取到任何消息,則拋出“Queue.Empty”異常
- 如果block值為False,消息隊(duì)列如果為空,則會(huì)立刻拋出“Queue.Empty”異常。
- Queue.get_nowait() :相當(dāng)于Queue.get(False)
- Queue.put(item, [block, [, timeout]]) :將item消息寫入隊(duì)列,block默認(rèn)值為True
- 如果block使用默認(rèn)值,且沒有設(shè)置timeout,消息隊(duì)列如果已沒有空間寫入,此時(shí)程序?qū)⒈蛔枞ㄍT趯懭霠顟B(tài)),直到從消息隊(duì)列中騰出空間為止,如果設(shè)置了True和timeout,則會(huì)等待timeout秒,若還沒有空間,則拋出”Queue.Full“異常
- 如果block值為False,消息隊(duì)列如果沒有空間可寫入,則會(huì)立刻拋出”Queue.Full“異常
- Queue.put_nowait(itme) :相當(dāng)于Queue.put(item, False)
import time
from multiprocessing import Queue, Process
def write(q)
for value in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']:
print("開始寫入:", value)
q.put(value)
time.sleep(1)
def read(q):
while True:
if not q.empty():
print("讀取到的是", q.get())
time.sleep(1)
else:
break
if __name__ == '__main__':
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
pw.start()
time.sleep(1)
pr.start()
pw.join()
pr.join()
print('接收完畢!')
六、進(jìn)程池之間通信- Manager().Queue()
- 進(jìn)程池之間通信,就需要使用multiprocessing.Manager()中的Queue()而不是multiprocessing.Queue()
- 否則會(huì)得到一條如下錯(cuò)誤信息:
RuntimeError: Queue objects should only be shared between processes through inheritance.
import time
from multiprocessing import Manager, Pool
def writer(q):
for i in "welcome":
print("開始寫入", i)
q.put(i)
def reader(q):
time.sleep(3)
for i in range(q.qsize()):
print("得到消息", q.get())
if __name__ == "__main__":
print("主進(jìn)程啟動(dòng)")
q = Manager().Queue()
po = Pool()
po.apply_async(writer, (q,))
po.apply_async(reader, (q,))
po.close()
po.join()