Python中多程序的使用 Python的多執行緒(threading)與多程序(multiprocessing )示例程式碼 Python多程序程式設計
阿新 • • 發佈:2019-01-04
- 程序:程式的一次執行(程式載入記憶體,系統分配資源執行)。每個程序有自己的記憶體空間,資料棧等,程序之間可以進行通訊,但是不能共享資訊。
- 執行緒:所有的執行緒執行在同一個程序中,共享相同的執行環境。每個獨立的執行緒有一個程式入口,順序執行序列和程式的出口。
- 執行緒的執行可以被強佔,中斷或者暫時被掛起(睡眠),讓其他的執行緒執行。一個程序中的各個執行緒共享同一片資料空間。
python中的多執行緒其實並不是真正的多執行緒,如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多程序。Python提供了非常好用的多程序包multiprocessing,只需要定義一個函式,Python會完成其他所有事情。藉助這個包,可以輕鬆完成從單程序到併發執行
多執行緒比較適合IO密集型,不太適合CPU密集型的任務。
#queue 多執行緒各個執行緒的運算的值放到一個佇列中,到主執行緒的時候再拿出來,以此來代替 #return的功能,因為線上程是不能返回一個值的 import time import threading from Queue import Queue def job(l,q): q.put([i**2 for i in l]) def multithreading(data): q= Queue() threads = [] for i in xrange(4): t = threading.Thread(target = job,args = (data[i],q)) t.start() threads.append(t) for thread in threads: thread.join() results = [] for _ in range(4): results.append(q.get()) print resultsif __name__ == "__main__": data = [[1,2,3],[4,5,6],[3,4,3],[5,5,5]] multithreading(data) [[1, 4, 9], [16, 25, 36], [9, 16, 9], [25, 25, 25]]
全域性直譯器鎖GIL(Global Interpreter Lock)
GIL並不是Python的特性,他是CPython引入的概念,是一個全域性排他鎖。
解釋執行python程式碼時,會限制執行緒對共享資源的訪問,直到直譯器遇到I/O操作或者操作次數達到一定數目時才會釋放GIL。 所以,雖然CPython的執行緒庫直接封裝了系統的原生執行緒,但CPython整體作為一個程序,同一時間只會有一個獲得GIL的執行緒在跑,其他執行緒則處於等待狀態。這就造成了即使在多核CPU中,多執行緒也只是做著分時切換而已,所以多執行緒比較適合IO密集型,不太適合CPU密集型的任務。同一時刻一個解釋程序只有一行 bytecode 在執行多程序
multiprocessing庫彌補了thread庫因為GIL而低效的缺陷。完整的複製了一套thread所提供的介面方便遷移,唯一的不同就是他使用了多程序而不是多執行緒。每個程序都有自己獨立的GIL。但是在windows下多程序的開銷要比多執行緒要大好多,Linux下是差不多的。多程序更加穩定;
建立函式並將其作為單個程序
import multiprocessing import time def worker(interval): n = 5 while n > 0: print("The time is {0}".format(time.ctime())) time.sleep(interval) n -= 1 if __name__ == "__main__": p = multiprocessing.Process(target = worker, args = (3,)) p.start() print "p.pid:", p.pid print "p.name:", p.name print "p.is_alive:", p.is_alive()
- 使用執行緒池
# 程序池 ,Pool中是有return的 import multiprocessing as mp def job(x): return x ** 2 def multiprocess(): pool = mp.Pool() # 預設是有幾個核就用幾個,可以自己設定processes = ? res = pool.map(job, range(10)) # 可以放入可迭代物件,自動分配程序 print(res)
# apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]])是阻塞的 res = pool.apply_async(job, (2,)) # 一次只能在一個程序裡計算,要達到map的效果,要迭代 print(res.get()) multi_res = [pool.apply_async(job, (i,)) for i in range(10)] # 迭代器 print([res.get() for res in multi_res]) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # 4 # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- fork操作:呼叫一次,返回兩次。作業系統自動把當前程序複製一份,分佈在父程序和子程序中返回,子程序永遠返回0,父程序永遠返回子程序的ID。子程序getppid()就可以拿到父程序的ID;