對(duì)多進(jìn)程的補(bǔ)充
對(duì)比下面兩段代碼
>>> if __name__=='__main__':
... print('Parent process %s.' % os.getpid())
... p = Process(target=run_proc, args=('test',))
... print('Child process will start.')
... p.start()
... p.join()
... print('Child process end.')
...
Parent process 2404.
Child process will start.
Run child process test (2414)...
Child process end.```
if name=='main':
... print('Parent process %s.' % os.getpid())
... p = Process(target=run_proc, args=('test',))
... print('Child process will start.')
... p.start()
... #p.join()
... print('Child process end.')
...
Parent process 2404.
Child process will start.
Child process end.
Run child process test (2415)...
1.`join()`方法可以等待子進(jìn)程結(jié)束后再繼續(xù)往下運(yùn)行,通常用于進(jìn)程間的同步。
否則會(huì)先同步進(jìn)行,子程序后面的父程序部分就會(huì)提前運(yùn)行。
2.上一篇筆記的問(wèn)題中,fork的時(shí)候是直接同時(shí)運(yùn)行了兩個(gè)程序,但是只有一個(gè)界面顯示,所以才會(huì)出現(xiàn)上一篇筆記所說(shuō)的結(jié)果。
所以一般用py文件來(lái)運(yùn)行
3.Pool的默認(rèn)大小是CPU的核數(shù)。
import time
from multiprocessing import Pool
def A():
for i in range(2):
print(i)
time.sleep(1)
pool = Pool(3) #定義進(jìn)程池大小
for i in range(6):
pool.apply_async(A)
以上循環(huán)在命令行界面會(huì)輸出六個(gè)函數(shù)類型,但其實(shí)在py文件中是沒(méi)有輸出的
pool.close() #關(guān)閉Pool,使其不再接受新的任務(wù),并不是退出子程序的意思
pool.join() #主進(jìn)程阻塞,等待子進(jìn)程的退出 否則會(huì)把主進(jìn)程和子進(jìn)程同時(shí)運(yùn)行,那么就會(huì)把p.join 后面的內(nèi)容同時(shí)打印。這樣可以等子進(jìn)程結(jié)束后在打印后面內(nèi)容
pool.join()和process的join不同
一個(gè)必須要start后才可以join,一個(gè)必須要close后才可以join
####多線程
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(1)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if name=='main':
print('Parent process %s.' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
結(jié)果:
Parent process 2439.
Waiting for all subprocesses done...
Run task 0 (2440)...
Run task 1 (2441)...
Run task 2 (2442)...
Run task 3 (2443)...
Task 3 runs 1.00 seconds.
Task 1 runs 1.00 seconds.
Task 0 runs 1.00 seconds.
Task 2 runs 1.00 seconds.
Run task 4 (2440)...
Task 4 runs 1.00 seconds.
All subprocesses done.
1.由于任何進(jìn)程默認(rèn)就會(huì)啟動(dòng)一個(gè)線程,我們把該線程稱為主線程,主線程又可以啟動(dòng)新的線程,Python的threading模塊有個(gè)current_thread()函數(shù),它永遠(yuǎn)返回當(dāng)前線程的實(shí)例。
2.主線程實(shí)例的名字叫MainThread,子線程的名字在創(chuàng)建時(shí)指定,我們用LoopThread命名子線程。名字僅僅在打印時(shí)用來(lái)顯示,完全沒(méi)有其他意義,如果不起名字Python就自動(dòng)給線程命名為Thread-1,Thread-2……
3.threading的thread 就相當(dāng)于 multiprocessing中的Process
4.線程的調(diào)度是由操作系統(tǒng)決定的
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
同一進(jìn)程的兩個(gè)線程可以同時(shí)更改一個(gè)變量。如果我們要確保計(jì)算正確,就要給函數(shù)上一把鎖,該線程因?yàn)楂@得了鎖,因此其他線程不能同時(shí)執(zhí)行只能等待,直到鎖被釋放后,獲得該鎖以后才能改。由于鎖只有一個(gè),無(wú)論多少線程,同一時(shí)刻最多只有一個(gè)線程持有該鎖,所以,不會(huì)造成修改的沖突。創(chuàng)建一個(gè)鎖就是通過(guò)threading.Lock()來(lái)實(shí)現(xiàn):
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要獲取鎖:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要釋放鎖:
lock.release()
這里上鎖的是代碼
當(dāng)多個(gè)線程同時(shí)執(zhí)行`lock.acquire()`時(shí),只有一個(gè)線程能成功地獲取鎖,然后繼續(xù)執(zhí)行代碼,其他線程就繼續(xù)等待直到獲得鎖為止。
獲得鎖的線程用完后一定要釋放鎖,否則那些苦苦等待鎖的線程將永遠(yuǎn)等待下去,成為死線程。所以我們用`try...finally`來(lái)確保鎖一定會(huì)被釋放。
鎖的好處就是確保了某段關(guān)鍵代碼只能由一個(gè)線程從頭到尾完整地執(zhí)行,壞處當(dāng)然也很多,首先是阻止了多線程并發(fā)執(zhí)行,包含鎖的某段代碼實(shí)際上只能以單線程模式執(zhí)行,效率就大大地下降了。其次,由于可以存在多個(gè)鎖,不同的線程持有不同的鎖,并試圖獲取對(duì)方持有的鎖時(shí),可能會(huì)造成死鎖,導(dǎo)致多個(gè)線程全部掛起,既不能執(zhí)行,也無(wú)法結(jié)束,只能靠操作系統(tǒng)強(qiáng)制終止。
**所以最好不要同時(shí)用多把鎖**
另外,Python解釋器由于設(shè)計(jì)時(shí)有GIL全局鎖,導(dǎo)致了多線程無(wú)法利用多核。
Python雖然不能利用多線程實(shí)現(xiàn)多核任務(wù),但可以通過(guò)多進(jìn)程實(shí)現(xiàn)多核任務(wù)。多個(gè)Python進(jìn)程有各自獨(dú)立的GIL鎖,互不影響。