多進(jìn)程
什么是多任務(wù)
什么叫做多任務(wù)呢?簡單的說,就是操作系統(tǒng)(OS)可以同時運(yùn)行多個任務(wù)。如你可以一邊瀏覽網(wǎng)頁,一邊聽著歌,同時還可以使用畫板畫著畫,這個就是多任務(wù)。其實我們的操作系統(tǒng)就是多任務(wù)。
單核CPU:
1、時間片輪換
2、優(yōu)先級別調(diào)度
多核CPU:
1、并發(fā)
2、并行
Linux下fork實現(xiàn)多進(jìn)程
進(jìn)程:
???????◆編寫完的代碼,在沒有運(yùn)行的情況下,稱之為程序。
???????◆正在運(yùn)行的代碼,就成為了進(jìn)程。
???????◆進(jìn)程,除了包含代碼外,還需要運(yùn)行環(huán)境等,所以和程序是存在區(qū)別的。
???????◆進(jìn)行多進(jìn)程操作時,肯定會存在一個主進(jìn)程。
fork()方法
Python在os模塊上封裝了常用的系統(tǒng)調(diào)用,其中就包括了fork,我們可以很輕松地在Python代碼中創(chuàng)建進(jìn)程。fork() 函數(shù)可以獲得系統(tǒng)中進(jìn)程的PID ( Process ID ),返回0則為子進(jìn)程,否則就是父進(jìn)程,然后可以據(jù)此對運(yùn)行中的進(jìn)程進(jìn)行操作;但是os.fork()只能在Linux下運(yùn)行,可以使用os.getpid()獲取該進(jìn)程的進(jìn)程號,os.getppid()獲取子進(jìn)程的父進(jìn)程進(jìn)程號。
import os
pid = os.fork()
if pid == 0:
while True:
print("執(zhí)行子進(jìn)程!我的進(jìn)程id:%s,我的父進(jìn)程id:%s" % (os.getpid(),os.getppid()))
else:
while True:
print("執(zhí)行主進(jìn)程!我的進(jìn)程id:%s,我的子進(jìn)程id:%s" % (os.getpid(),pid))
#運(yùn)行結(jié)果
>>>執(zhí)行子進(jìn)程!我的進(jìn)程id:2978,我的父進(jìn)程id:2977
>>>執(zhí)行主進(jìn)程!我的進(jìn)程id:2977,我的子進(jìn)程id:2978
>>>執(zhí)行主進(jìn)程!我的進(jìn)程id:2977,我的子進(jìn)程id:2978
>>>執(zhí)行子進(jìn)程!我的進(jìn)程id:2978,我的父進(jìn)程id:2977
>>>...
#假如多進(jìn)程中有全局變量的使用
import os
pid = os.fork()
num = 0
if pid == 0:
num +=1
print("子進(jìn)程" + num)
else:
num += 1
print("父進(jìn)程" + num)
#運(yùn)行結(jié)果
>>>子進(jìn)程1
>>>父進(jìn)程1
#當(dāng)多個fork存在時的運(yùn)行結(jié)果
import os
pid1 = os.fork()
if pid1 == 0:
print("我是進(jìn)程1")
else:
print("我是進(jìn)程2")
pid2 = os.fork()
if pid2 == 0:
print("我是進(jìn)程3")
else:
pirnt("我是進(jìn)程4")
#運(yùn)行結(jié)果
>>>我是進(jìn)程1
>>>我是進(jìn)程3
>>>我是進(jìn)程4
>>>我是進(jìn)程2
>>>我是進(jìn)程3
>>>我是進(jìn)程4
觀察運(yùn)行結(jié)果發(fā)現(xiàn):
???????◆兩個進(jìn)程的運(yùn)行的順序是無序的;
???????◆進(jìn)程之間的數(shù)據(jù)無法共享,是各自擁有的一份;
???????◆當(dāng)程序遇到fork時程序會被分成一個子進(jìn)程一個主進(jìn)程,然后再次遇到fork子進(jìn)程又被分成一個主進(jìn)程和一個子進(jìn)程,主進(jìn)程也被分成一個子進(jìn)程和主進(jìn)程,就產(chǎn)生了上面的第三個運(yùn)行結(jié)果;

函數(shù)多進(jìn)程
multiprocessing引入多進(jìn)程
fork()方法只能使用在linux系統(tǒng)下(tornado框架的高效性就是使用一個單線程通過fork()實現(xiàn)同時服務(wù)多個客戶端)
python中在linux和windows下都能使用的多線程是multiprocessing中的Process類,通過Process實現(xiàn)一個多進(jìn)程
# 引入模塊
from multiprocessing import Process
import time
# 定義一個函數(shù),將來定義成一個進(jìn)程
def pro1(msg):
for i in range(5):
print("進(jìn)程%s正在運(yùn)行" % msg )
time.sleep(1)
else:
print("進(jìn)程%s已結(jié)束" % msg)
def pro2(msg):
for i in range(10):
print("進(jìn)程%s正在運(yùn)行" % msg )
time.sleep(1)
else:
print("進(jìn)程%s已結(jié)束" % msg)
# 主進(jìn)程開始,然后啟動子進(jìn)程
if __name__ == "__main__":
"""
1、多進(jìn)程必須需要主進(jìn)程才能執(zhí)行,python中__main__就是主進(jìn)程的位置
2、將定義好的函數(shù)轉(zhuǎn)換成進(jìn)程,只需要將Process類實例化,必備屬性有:
①target:Process的一個屬性,值是要轉(zhuǎn)換成子進(jìn)程的函數(shù)名
②args:它必須是一個元組,保存所有要用到的參數(shù),若只有一個參數(shù),寫成(val,)的形式
"""
print("————————主進(jìn)程啟動————————")
bilibili = Process(target=pro1,args=("嗶哩嗶哩",),name="嗶哩嗶哩干杯")
dnf = Process(target=pro2,args=("掉線城與勇士",),name="CNM給我出時光鞋")
# 開啟子進(jìn)程
print(bilibili.name)
bilibili.start()
# 這里可以使用一下進(jìn)程對象的terminate方法,終止一個進(jìn)程
if bilibili.is_alive():
bilibili.terminate()
print("嗶哩嗶哩已結(jié)束")
print(dnf.name)
dnf.start()
# 設(shè)置主進(jìn)程等待子進(jìn)程結(jié)束在結(jié)束自己本身,當(dāng)有多個進(jìn)程時
# 等待哪個進(jìn)程就會在哪個進(jìn)程結(jié)束后向下執(zhí)行,
bilibili.join()
print("————————主進(jìn)程結(jié)束————————")
# 運(yùn)行結(jié)果
D:\python軟件\python.exe D:/python_test/re_test.py
————————主進(jìn)程啟動————————
嗶哩嗶哩干杯
嗶哩嗶哩已結(jié)束
CNM給我出時光鞋
————————主進(jìn)程結(jié)束————————
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士正在運(yùn)行
進(jìn)程掉線城與勇士已結(jié)束
Process finished with exit code 0
總結(jié):
1、主進(jìn)程是必須的
2、創(chuàng)建進(jìn)程實例時target目標(biāo)是函數(shù)名不是調(diào)用函數(shù)
3、args屬性必須是一個元組,只有一個參數(shù)需要時寫成(val,)
4、termanite()終止方法,結(jié)束進(jìn)程
5、多個子進(jìn)程存在時,設(shè)置主進(jìn)程等待哪個子進(jìn)程,就在哪個子進(jìn)程結(jié)束后繼續(xù)向下執(zhí)行主進(jìn)程
類的多進(jìn)程
創(chuàng)建一個子進(jìn)程類的步驟
1、定義一個類,繼承自Process父類
2、如果需要父類中的某些屬性使用supper().__init__()或Process.__init__()調(diào)用
3、重寫Process父類的run方法,在run方法中寫入需要執(zhí)行的進(jìn)程
from multiprocessing import Process
class MyProcess(Process):
def __init__(self,name,msg):
super().__init__(name=name)
self.msg = msg
#重寫run方法
def run(self):
print(self.name)
print("子進(jìn)程%s開始運(yùn)行" % self.msg)
# 主進(jìn)程入口
if __name__ == "__main__":
print("————————主進(jìn)程開始————————")
# 定義出子進(jìn)程的實例,并執(zhí)行子進(jìn)程
for i in range(5):
m = MyProcess("bilibli","嗶哩嗶哩動畫")
m.start()
m.join()
print("————————主進(jìn)程結(jié)束————————")
#運(yùn)行結(jié)果
D:\python軟件\python.exe D:/python_test/re_test.py
————————主進(jìn)程開始————————
bilibli
子進(jìn)程嗶哩嗶哩動畫開始運(yùn)行
bilibli
子進(jìn)程嗶哩嗶哩動畫開始運(yùn)行
bilibli
子進(jìn)程嗶哩嗶哩動畫開始運(yùn)行
bilibli
子進(jìn)程嗶哩嗶哩動畫開始運(yùn)行
bilibli
子進(jìn)程嗶哩嗶哩動畫開始運(yùn)行
————————主進(jìn)程結(jié)束————————
Process finished with exit code 0
進(jìn)程池申請異步
進(jìn)程池概念
當(dāng)我們有很多大量的進(jìn)程需要執(zhí)行時(例如多個用戶訪問服務(wù)器,每個用戶都是一個進(jìn)程);
此時若繼續(xù)使用Process創(chuàng)建多進(jìn)程,需要多次創(chuàng)建,大大加重了內(nèi)存的使用和代碼量;
使用進(jìn)程池Pool保存進(jìn)程,能提前創(chuàng)建規(guī)定數(shù)量的進(jìn)程,通過遍歷一次得到多個進(jìn)程;
達(dá)到并發(fā)并行的效果
定義一個簡單的進(jìn)程池
"""
按照順序用進(jìn)程池申請異步的方法——apply_async()
"""
#引入需要的模塊
from multiprocessing import Pool
# 定義進(jìn)程對象的目標(biāo)函數(shù)
def run(msg):
print("進(jìn)程%s運(yùn)行了" %msg)
#主進(jìn)程入口
if __name__ == "__main__":
print("主進(jìn)程開始")
# 定義一個進(jìn)程池,池中保存3個進(jìn)程
p = Pool(3)
# 遍歷進(jìn)程池獲取一次獲取3個進(jìn)程
for i in range(10):
#進(jìn)程池實例的apply_async()方法申請異步,實現(xiàn)并發(fā)并行,參數(shù)有target,args,name等
p.apply_async(run,("開啟session",))
#執(zhí)行完畢后一定要關(guān)閉進(jìn)程池
p.close()
#這里是重點(diǎn),進(jìn)程池中子進(jìn)程類似于守護(hù)進(jìn)程,
#當(dāng)主進(jìn)程結(jié)束后,子進(jìn)程自動結(jié)束,所以必須設(shè)置主進(jìn)程等待
p.join()
print("主進(jìn)程結(jié)束")
#運(yùn)行結(jié)果
D:\python軟件\python.exe D:/_web_project/tetetetetet.py
主進(jìn)程開始
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
進(jìn)程開啟session運(yùn)行了
主進(jìn)程結(jié)束
Process finished with exit code 0
總結(jié)重點(diǎn)
1、定義進(jìn)程池實例時要設(shè)定池中一次性存放幾個子進(jìn)程;
2、執(zhí)行結(jié)果是一次出現(xiàn)3個然后出現(xiàn)3次,最后一次只出現(xiàn)了一個;
3、子進(jìn)程執(zhí)行完畢后,必須關(guān)閉進(jìn)程池;
4、主進(jìn)程必須等待進(jìn)程池中的子進(jìn)程執(zhí)行完畢后再結(jié)束?!臼刈o(hù)進(jìn)程:跟隨主進(jìn)程的結(jié)束而結(jié)束】
進(jìn)程中的數(shù)據(jù)傳遞和數(shù)據(jù)共享
在上面的案例中可以看出,進(jìn)程間的數(shù)據(jù)都是單獨(dú)的一份,不會發(fā)生影響;但是進(jìn)程之間的數(shù)據(jù)并不是絕對隔離的,可以實現(xiàn)使用Queue進(jìn)行進(jìn)程的數(shù)據(jù)傳遞,使用Manager實現(xiàn)進(jìn)程間的數(shù)據(jù)共享。
Queue數(shù)據(jù)傳遞
Queue是一種特殊的先進(jìn)先出的線性數(shù)據(jù)表,一般的數(shù)據(jù)隊列都有大小限制,等待超時時間需要自己定義,常用方法有:
?????◆put()放入數(shù)據(jù)到隊列中
?????◆get()從隊列中取出數(shù)據(jù)
?????◆empty()清空隊列
from multiprocessing import Process,Queue
#子進(jìn)程
def put_pro(q,n):
#向隊列添加數(shù)據(jù)
q.put("第%s個進(jìn)程添加了一個數(shù)據(jù)" % n)
#主進(jìn)程
def get_pro(q):
print("————————————start————————————")
for i in range(5):
p = Process(target=put_pro,args=(q,i+1))
p.start()
#是否收到子進(jìn)程的數(shù)據(jù)
print(q.get())
p.join()
print("————————————end—————————————")
#程序入口
if __name__ == "__main__":
#定義一個隊列實例
q = Queue()
#啟用多進(jìn)程
get_pro(q)
>>>運(yùn)行結(jié)果
D:\python軟件\python.exe D:/_web_project/tetetetetet.py
————————————start————————————
第1個進(jìn)程添加了一個數(shù)據(jù)
第2個進(jìn)程添加了一個數(shù)據(jù)
第3個進(jìn)程添加了一個數(shù)據(jù)
第4個進(jìn)程添加了一個數(shù)據(jù)
第5個進(jìn)程添加了一個數(shù)據(jù)
————————————end—————————————
Process finished with exit code 0
Manager實現(xiàn)進(jìn)程間數(shù)據(jù)共享
from multiprocessing import Manager 數(shù)據(jù)管理器類,一個管理器類可以創(chuàng)建用于進(jìn)程間數(shù)據(jù)共享的各種對象,例如:dict,list,queue,Array,Lock等,有了管理器就可以根據(jù)需要定義數(shù)據(jù)類型,減少局限性
from multiprocessing import Process,Manager
# 定義兩個子進(jìn)程,分別向全局變量中添加數(shù)據(jù)
def add_one(d,ls,n):
d["name%s" %n] = "進(jìn)程一%s" % n
ls.append(n)
print("進(jìn)程1執(zhí)行了",d,ls)
def add_two(d,ls,n):
d["name%s" %n] = "進(jìn)程二%s" % n
ls.append(n)
print("進(jìn)程2執(zhí)行了",d,ls)
def run(d,ls):
for i in range(3):
p1 = Process(target=add_one,args=(d,ls,i))
p1.start()
p1.join()
p2 = Process(target=add_two,args=(d,ls,i))
p2.start()
p2.join()
#輸出執(zhí)行完畢的全局變量
print(d,ls)
if __name__ == "__main__":
#定義幾個全局的Manager變量
d = Manager().dict()
ls = Manager().list()
run(d,ls)
>>>運(yùn)行結(jié)果
D:\python軟件\python.exe D:/_web_project/tetetetetet.py
進(jìn)程1執(zhí)行了 {'name0': '進(jìn)程一0'} [0]
進(jìn)程2執(zhí)行了 {'name0': '進(jìn)程二0'} [0, 0]
進(jìn)程1執(zhí)行了 {'name0': '進(jìn)程二0', 'name1': '進(jìn)程一1'} [0, 0, 1]
進(jìn)程2執(zhí)行了 {'name0': '進(jìn)程二0', 'name1': '進(jìn)程二1'} [0, 0, 1, 1]
進(jìn)程1執(zhí)行了 {'name0': '進(jìn)程二0', 'name1': '進(jìn)程二1', 'name2': '進(jìn)程一2'} [0, 0, 1, 1, 2]
進(jìn)程2執(zhí)行了 {'name0': '進(jìn)程二0', 'name1': '進(jìn)程二1', 'name2': '進(jìn)程二2'} [0, 0, 1, 1, 2, 2]
{'name0': '進(jìn)程二0', 'name1': '進(jìn)程二1', 'name2': '進(jìn)程二2'} [0, 0, 1, 1, 2, 2]
Process finished with exit code 0
————————————————————————————————————————————————————————————————————
#如果將程序中的管理器字典和管理器列表變成{}和[],運(yùn)行結(jié)果為
進(jìn)程1執(zhí)行了 {'name0': '進(jìn)程一0'} [0]
進(jìn)程2執(zhí)行了 {'name0': '進(jìn)程二0'} [0]
進(jìn)程
1執(zhí)行了 {'name1': '進(jìn)程一1'} [1]
進(jìn)程2執(zhí)行了 {'name1': '進(jìn)程二1'} [1]
進(jìn)程1執(zhí)行了 {'name2': '進(jìn)程一2'} [2]
進(jìn)程2執(zhí)行了 {'name2': '進(jìn)程二2'} [2]
{} []
python的多進(jìn)程操作大概就是這樣,對于python來說由于cpython解釋器全局鎖的存在,python的多線程并不能發(fā)揮它的威力,熟練掌握多進(jìn)程是很重要的
