進(jìn)程介紹和Process
1、什么是進(jìn)程
- 一個(gè)程序運(yùn)行起來(lái)后门躯,它的代碼以及需要使用到的資源,稱之為進(jìn)程酷师,它是操作系統(tǒng)分配資源的基本單元讶凉,還是線程的容器,一個(gè)進(jìn)程中會(huì)有一個(gè)或多個(gè)線程
- 不僅是可以通過(guò)線程來(lái)完成多任務(wù)操作山孔,進(jìn)程也是可以的
2懂讯、進(jìn)程的狀態(tài)
- 工作中,任務(wù)數(shù)往往會(huì)大于CPU的核數(shù)饱须。那么肯定有一部分任務(wù)正在執(zhí)行域醇,而另外一部分任務(wù)在等待CPU的資源分配,因此就導(dǎo)致進(jìn)程會(huì)有不同的狀態(tài)
a.就緒狀態(tài):運(yùn)行的條件都已經(jīng)滿足蓉媳,正在等待CPU的資源分配過(guò)來(lái)
b.執(zhí)行狀態(tài):CPU正在執(zhí)行該進(jìn)程
c.等待狀態(tài):在等待某些條件滿足譬挚。例如:一個(gè)程序正在sleep,此時(shí)就是等待狀態(tài)
3酪呻、進(jìn)程减宣、線程進(jìn)行對(duì)比
- 功能
a.進(jìn)程:能夠完成多任務(wù);比如在一臺(tái)電腦上能夠同時(shí)運(yùn)行多個(gè)軟件
b.線程:能夠完成多任務(wù)玩荠;比如一個(gè)瀏覽器可以開(kāi)多個(gè)窗口 - 定義的不同
a.進(jìn)程:是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位
b.線程:線程是進(jìn)程的一個(gè)實(shí)體漆腌,是CPU進(jìn)行調(diào)度和分派的基本單位。它是比進(jìn)程更小的能獨(dú)立運(yùn)行的單位阶冈。線程自己基本上不擁有系統(tǒng)資源闷尿,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(比如程序計(jì)數(shù)器,一組寄存器和棧)女坑,但是它可以和同屬一個(gè)進(jìn)程之間的其它線程共享該進(jìn)程所擁有的全部資源 - 區(qū)別
a.一個(gè)程序至少有一個(gè)進(jìn)程填具,一個(gè)進(jìn)程至少有一個(gè)線程
b.線程的劃分尺度小于進(jìn)程(資源比進(jìn)程少),使得多線程程序的并發(fā)性高
c.進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存空間匆骗,而該進(jìn)程的多個(gè)線程可以共享這個(gè)內(nèi)存空間劳景,從而極大提高了程序的運(yùn)行效率
d.不同進(jìn)程之間的內(nèi)存空間是獨(dú)立的
e.線程不能夠獨(dú)立運(yùn)行,必須依賴于進(jìn)程 - 優(yōu)缺點(diǎn)
a.線程的執(zhí)行開(kāi)銷小碉就,但是不利于資源的管理和保護(hù)(線程之間資源共享)
b.進(jìn)程的執(zhí)行開(kāi)銷大盟广,但是有利于資源的管理和保護(hù)(進(jìn)程之間資源不共享)
4、multiprocessing
- 官方說(shuō)明
multiprocessing
是一個(gè)用與threading
模塊相似API的支持產(chǎn)生進(jìn)程的包.multiprocessing
包同時(shí)提供本地和遠(yuǎn)程并發(fā)瓮钥,使用子進(jìn)程代替線程筋量,有效避免Global Interpreter Lock
帶來(lái)的影響烹吵。因此,multiprocessing
模塊允許程序員充分利用機(jī)器上的多個(gè)核心毛甲。Unix 和 Windows 上都可以運(yùn)行旷偿。 - Process類
通過(guò)創(chuàng)建一個(gè)Process
對(duì)象然后調(diào)用它的start()
方法來(lái)生成進(jìn)程邻梆。Process
和threading.Thread
的API相同揍移。
-
Process(group, target, name, args, kwargs)
:
group
:指定進(jìn)程組滑肉,大部分情況使用不到
target
:如果傳遞了函數(shù)的引用,這個(gè)子進(jìn)程就會(huì)執(zhí)行這個(gè)函數(shù)
name
:給子進(jìn)程設(shè)置名字七咧,可不設(shè)置
args
:給target指定的函數(shù)傳遞參數(shù)跃惫,tuple
kwargs
:給target指定的函數(shù)傳遞參數(shù),dict -
Process
創(chuàng)建的實(shí)例對(duì)象的常用方法:
start()
:?jiǎn)?dòng)子進(jìn)程實(shí)例(創(chuàng)建子進(jìn)程)
is_alive()
:判斷進(jìn)程的子進(jìn)程是否還存活
join(timeout)
:是否等待子進(jìn)程執(zhí)行結(jié)束艾栋,或者等待指定的秒數(shù)爆存,然后再進(jìn)行下面的代碼
terminate()
:不管任務(wù)是否完成,立刻終止子進(jìn)程
3.Process
創(chuàng)建的實(shí)例對(duì)象的常用屬性:
name
:當(dāng)前進(jìn)程的別名蝗砾,默認(rèn)為Process-N先较,N是從1開(kāi)始遞增的整數(shù)
pid
:當(dāng)前進(jìn)程的pid(進(jìn)程號(hào)),可以用os.getpid()
查看
5悼粮、代碼例子
- 1闲勺、使用進(jìn)程執(zhí)行簡(jiǎn)單任務(wù)
import time
from multiprocessing import Process
def work1():
print('喝水5秒')
for i in range(5):
time.sleep(1)
print('喝水中...')
def work2():
print('澆花4秒')
for i in range(4):
time.sleep(1)
print('澆花中...')
""" 使用多進(jìn)程來(lái)執(zhí)行多任務(wù)"""
if __name__ == '__main__': # Process在windows系統(tǒng)運(yùn)行必須在__main__中,mac則不需要
p1 = Process(target=work1)
p2 = Process(target=work2)
p1.start()
p2.start()
- 2扣猫、
Process
執(zhí)行的注意事項(xiàng)
Process
在windows系統(tǒng)運(yùn)行必須在__main__
塊中菜循,mac則不需要
去掉__main__直接運(yùn)行
加入__main__進(jìn)行運(yùn)行
- 3、創(chuàng)建帶參的進(jìn)程類
方式和Thread
一樣申尤,重寫__init__
和run
方法
from multiprocessing import Process
class MyPrecess(Process):
def __init__(self, value):
self.value = value
super().__init__()
def run(self):
for i in range(10):
print(F'進(jìn)程{self.name}的{self.value}在跑第{i}次') # 獲取進(jìn)程名
if __name__ == '__main__':
p = []
p1 = MyPrecess(value='lzl')
p2 = MyPrecess(value='lzl')
p.append(p1)
p.append(p2)
p1.start()
p[0].terminate() # 終止p1
p2.start()
- 4癌幕、多進(jìn)程操作全局變量
1.進(jìn)程各自擁有自己的變量,不會(huì)對(duì)全局變量有影響昧穿。比如1個(gè)主進(jìn)程中有2個(gè)子進(jìn)程勺远,那么這三個(gè)進(jìn)程都擁有自己的“全局變量”
2.下面例子中,number會(huì)有三個(gè)值:0时鸵、500000谚中、1000000
from multiprocessing import Process
number = 0
def work1():
for i in range(0, 1000000):
global number
number += 1
print(number)
def work2():
for i in range(0, 500000):
global number
number += 1
print(number)
if __name__ == '__main__':
p1 = Process(target=work1)
p2 = Process(target=work2)
p1.start()
p2.start()
print(number)
'''
三個(gè)值:
0
500000
1000000
'''
- 5、多進(jìn)程操作系統(tǒng)資源
1.文件是系統(tǒng)資源寥枝,進(jìn)程之間會(huì)共用,并會(huì)有資源競(jìng)爭(zhēng)
2.下面的例子中磁奖,跑出來(lái)的結(jié)果中囊拜。文件中的內(nèi)容并不是1000行java、1000行python
from multiprocessing import Process
def work1():
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('python\n')
def work2():
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('java\n')
if __name__ == '__main__':
p1 = Process(target=work1)
p2 = Process(target=work2)
p1.start()
p2.start()
p1.join()
p2.join()
3.出現(xiàn)這個(gè)bug的原因比搭,類似線程對(duì)共享資源的競(jìng)爭(zhēng)冠跷,進(jìn)程函數(shù)還在運(yùn)行時(shí),系統(tǒng)切換了進(jìn)程資源,導(dǎo)致上一個(gè)進(jìn)程的內(nèi)容還沒(méi)完全寫入到文件中蜜托。
不加鎖的結(jié)果
- 6抄囚、解決進(jìn)程之間對(duì)系統(tǒng)資源的競(jìng)爭(zhēng)關(guān)系
1.使用鎖,對(duì)進(jìn)程進(jìn)行串行處理橄务。同一時(shí)刻只能有一個(gè)進(jìn)程在執(zhí)行
2.因?yàn)槭沁M(jìn)程幔托,所以鎖對(duì)象只能通過(guò)參數(shù)來(lái)傳遞給進(jìn)程所調(diào)用的函數(shù)或類
3.使用鎖來(lái)解決上面例子的bug
from multiprocessing import Process
from multiprocessing import Lock
def work1(lock):
# 操作之前加鎖
lock.acquire()
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('python\n')
# 操作結(jié)束后釋放鎖
lock.release()
def work2(lock):
lock.acquire()
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('java\n')
lock.release()
if __name__ == '__main__':
lock = Lock()
p1 = Process(target=work1, args=(lock,))
p2 = Process(target=work2, args=(lock,))
p1.start()
p2.start()
p1.join()
p2.join()
結(jié)果:加了鎖的結(jié)果
6、進(jìn)程之間的通信
1蜂挪、進(jìn)程之間的通信使用的也是隊(duì)列重挑,不過(guò)并不是線程所使用的隊(duì)列
queue.Queue
:進(jìn)程內(nèi)的線程之間的隊(duì)列
multiprocess.Queue
:進(jìn)程之間的隊(duì)列2、
multiprocess.Queue
可以使用multiprocess.Queue
來(lái)實(shí)現(xiàn)多進(jìn)程之間的數(shù)據(jù)的傳遞棠涮,Queue本身是一個(gè)消息隊(duì)列程序谬哀。
注意:multiprocess.Queue
需要把它的實(shí)例對(duì)象,以參數(shù)的形式傳入到各個(gè)進(jìn)程任務(wù)中(不會(huì)被復(fù)制到進(jìn)程的內(nèi)存中严肪,只是傳遞過(guò)去)史煎,否則各個(gè)進(jìn)程使用的還是各自的隊(duì)列(也就是把隊(duì)列復(fù)制到各自的內(nèi)存里了),無(wú)法形成進(jìn)程之間的數(shù)據(jù)傳遞
from multiprocessing import Process
from multiprocessing import Queue
import os
q = Queue() # 創(chuàng)建隊(duì)列
# 給隊(duì)列添加數(shù)據(jù)
for i in range(5):
q.put(i)
"""如果不使用傳參的方式驳糯,那么兩個(gè)進(jìn)程之間使用的隊(duì)列是各自的隊(duì)列篇梭,不能完成通信"""
def work1(q): # 使用同一個(gè)隊(duì)列資源
while not q.empty():
print(F"進(jìn)程{os.getpid()}在獲取隊(duì)列中的數(shù)據(jù){q.get()}")
def work2(q): # 使用同一個(gè)隊(duì)列資源
while not q.empty():
print(F"進(jìn)程{os.getpid()}在獲取隊(duì)列中的數(shù)據(jù){q.get()}")
if __name__ == '__main__':
p1 = Process(target=work1, args=(q,))
p2 = Process(target=work2, args=(q,))
p1.start()
p2.start()
'''
結(jié)果:兩個(gè)進(jìn)程使用的是同個(gè)隊(duì)列的資源,實(shí)現(xiàn)了進(jìn)程之間的通信
進(jìn)程8380在獲取隊(duì)列中的數(shù)據(jù)0
進(jìn)程8380在獲取隊(duì)列中的數(shù)據(jù)1
進(jìn)程8380在獲取隊(duì)列中的數(shù)據(jù)2
進(jìn)程8380在獲取隊(duì)列中的數(shù)據(jù)3
進(jìn)程8380在獲取隊(duì)列中的數(shù)據(jù)4
'''