多任務(wù)的實(shí)現(xiàn)方式:
- 多進(jìn)程模式
- 多線程模式
- 多進(jìn)程 + 多線程 模式
python即支持多進(jìn)程拱绑,又支持多線程,如下進(jìn)行介紹果港。
多進(jìn)程
操作系統(tǒng)fork()系統(tǒng)調(diào)用復(fù)制當(dāng)前進(jìn)程廉羔,并且在父進(jìn)程和子進(jìn)程中返回,父進(jìn)程中返回fork出的子進(jìn)程ID塘揣, 子進(jìn)程中返回0;
一個(gè)父進(jìn)程可以fork很多子進(jìn)程包雀;
子進(jìn)程可以通過(guò)getppid()拿到父進(jìn)程ID;任何進(jìn)程都可以通過(guò)getpid()拿取當(dāng)前進(jìn)程ID;
python的os模塊封閉了常見(jiàn)的系統(tǒng)調(diào)用,其中就包括fork.
#/usr/bin/env python
# _*_ encoding: utf-8 _*_
import os
print 'Process (%s) start...' % os.getpid()
pid = os.fork()
if pid ==0:
print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())
else:
print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)
運(yùn)行結(jié)果:
Process (19929) start...
I (19929) just created a child process (19930).
I am child process (19930) and my parent is 19929.
由于Windows沒(méi)有fork調(diào)用亲铡,上面的代碼在Windows上無(wú)法運(yùn)行才写。
multiprocess--跨平臺(tái)的多進(jìn)程支持模塊
1.Process實(shí)例
#/usr/bin/env python
# _*_ encoding: utf-8 _*_
from multiprocessing import Process
import os
def run_proc(name):
print 'Run child process %s (%s)...' % (name, os.getpid())
if __name__ == '__main__':
print 'Parent process %s.' % os.getpid()
p = Process(target=run_proc, args=('test',))
print 'Process will start'
p.start() #啟動(dòng)子進(jìn)程
p.join() #等待子進(jìn)程結(jié)束再向下執(zhí)行
print 'Process end'
運(yùn)行結(jié)果
Parent process 24556.
Process will start
Run child process test (24557)...
Process end
2.Pool實(shí)例--進(jìn)程池實(shí)現(xiàn)
#!/usr/bin/env python
# _*_ encoding: utf-8 _*_
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(random.random() * 3)
end = time.time()
print 'Task %s runs %0.2f seconds.' % (name, (end - start))
if __name__ == '__main__':
print 'Parent process %s.' % os.getpid()
p = Pool()
for i in range(5):
p.apply_async(long_time_task, args=(i, ))
print 'Waiting for all subprocesses done...'
p.close() #調(diào)用close()之后就不能繼續(xù)添加新的Process了
p.join()
print 'All subprocesses done.'
運(yùn)行結(jié)果
Parent process 27577.
Waiting for all subprocesses done...
Run task 0 (27579)...
Run task 1 (27580)...
Run task 2 (27581)...
Run task 3 (27582)...
Task 1 runs 0.15 seconds.
Run task 4 (27580)...
Task 3 runs 1.40 seconds.
Task 4 runs 1.27 seconds.
Task 0 runs 1.96 seconds.
Task 2 runs 2.04 seconds.
All subprocesses done.
請(qǐng)注意輸出的結(jié)果,task 0奖蔓,1赞草,2,3是立刻執(zhí)行的吆鹤,而task 4要等待前面某個(gè)task完成后才執(zhí)行厨疙,這是因?yàn)镻ool的默認(rèn)大小在我的電腦上是4,因此疑务,最多同時(shí)執(zhí)行4個(gè)進(jìn)程沾凄。這是Pool有意設(shè)計(jì)的限制,并不是操作系統(tǒng)的限制知允。(Pool的默認(rèn)大小是CPU的核數(shù))撒蟀,可以根據(jù)pool的第一個(gè)參數(shù)進(jìn)行修改:
p = Pool(5)
進(jìn)程間通信
Python中multiprocessing模塊包裝了底層的機(jī)制,提供了Queue温鸽、Pipes等方式來(lái)進(jìn)行進(jìn)程間數(shù)據(jù)交換保屯。
如何以Queue為示例:
#!/usr/bin/env python
# _*_ encoding: utf-8 _*_
from multiprocessing import Process, Queue
import os, time, random
# 寫進(jìn)程執(zhí)行的代碼:
def write(q):
for value in ['A', 'B', 'C']:
print 'Put %s to queue...' % value
q.put(value)
time.sleep(random.random())
# 讀進(jìn)程執(zhí)行的代碼:
def read(q):
while True:
value = q.get(True)
print 'Get %s from queue.' % value
if __name__=='__main__':
# 父進(jìn)程創(chuàng)建Queue,并傳給各個(gè)子進(jìn)程
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
pw.start()
pr.start()
# 等待PW結(jié)束
pw.join()
# 強(qiáng)行結(jié)束pr進(jìn)程
pr.terminate()
結(jié)果:
Put A to queue...
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.
多線程
python標(biāo)準(zhǔn)庫(kù)通過(guò)thread和threading實(shí)現(xiàn)多線程嗤朴;thread是低級(jí)模塊配椭;threading是高級(jí)模塊;
#!/usr/bin/env python
# _*_ encoding: utf-8 _*_
import time,threading
# 新線程執(zhí)行的代碼
def loop():
print 'thread %s is running...' % threading.current_thread().name
n = 0
while n < 5:
n = n + 1
print 'thread %s >>> %s' % (threading.current_thread().name, n)
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name
結(jié)果:
thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.
多線程程序需要解決的一個(gè)問(wèn)題就是多線程共享變量雹姊,造成修改混亂股缸。
需要通過(guò)threading.Lock()進(jìn)行加鎖解鎖。
寫在最后的坑:
python有一個(gè)GIL鎖吱雏,Global Interpreter Lock, 使所有的python多線程只能交替執(zhí)行敦姻,只能在1核心上跑瘾境。。镰惦。
在Python中迷守,可以使用多線程,但不要指望能有效利用多核旺入。如果一定要通過(guò)多線程利用多核兑凿,那只能通過(guò)C擴(kuò)展來(lái)實(shí)現(xiàn),不過(guò)這樣就失去了Python簡(jiǎn)單易用的特點(diǎn)茵瘾。
不過(guò)礼华,也不用過(guò)于擔(dān)心,Python雖然不能利用多線程實(shí)現(xiàn)多核任務(wù)拗秘,但可以通過(guò)多進(jìn)程實(shí)現(xiàn)多核任務(wù)圣絮。多個(gè)Python進(jìn)程有各自獨(dú)立的GIL鎖,互不影響雕旨。
所以Python推薦使用多進(jìn)程...
python支持分布式進(jìn)程
multiprocessing.managers子模塊支持把多進(jìn)程分布到多臺(tái)機(jī)器上;