摘要:主進程會等待所有的子進程先結束,然后再結束主進程。關閉進程池,關閉后實例不再接收新的請求等待實例中的所有子進程執行完畢,主進程才會退出,必須放在語句之后。主進程一般都用來等待,任務在子進程中執行。
多任務:同一個時間段中,執行多個函數/運行多個程序.
操作系統可以同時運行多個任務:
操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒……這樣反復執行下去。表面上看,每個任務都是交替執行的,但是,由于CPU的執行速度實在是太快了,感覺就像所有任務都在同時執行一樣。(時間片輪轉)
任務 執行算法:
時間片輪轉
優先級調度
調度算法(什么樣的情況下按照什么樣的規則,讓哪個任務執行)
真正的并行執行多任務只能在多核CPU上實現,但是,由于任務數量遠遠多于CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。
進程
線程
協程
并發:看上去一齊執行(任務數>內核數)
并行:真正一齊執行(內核數>任務數)
程序:編寫完畢的代碼,在沒有運行的時候(一個可執行的代碼,可以理解稱沒有生命)
進程:正在運行的代碼(除了包含代碼外,還需要運行環境,占用的內存,鍵盤,顯示器等,可以理解稱具有生命)
創建子進程
os.fork()創建新的進程,為子進程
import os import time ret = os.fork() # 返回二個特殊值, 其中一個等于0(子進程),一個不固定的大于0的值(父進程,pid)。都是int類型。 if ret == 0: while True: print("1") time.sleep(1) else: while True: print("2") time.sleep(1)
不一定父進程先執行,或子進程先執行,哪個進程先執行,是依靠操作系統調度算法。
Note: os.fork(),只在Unix/Linux/Mac上運行,windows不可以。
getpid、getppid
import os ret = os.fork() print(ret) if ret > 0: print("父進程 - %d"%os.getpid()) else: print("子進程 - %d - %d"%(os.getpid(), os.getppid())) """ 1535 父進程 - 1534 0 子進程 - 1535 - 1534 """
os.getpid(): 子進程的pid的值
os.getppid(): 父進程的pid的值
父進程中fork的返回值,就是剛剛創建出來的子進程的pid
父子進程的先后順序
主進程執行完結束后,子進程沒有結束。照樣主進程結束掉,而子進程一樣執行完程序。
import os import time ret = os.fork() if ret == 0: print("子進程") time.sleep(5) print("子進程over") else: print("父進程") time.sleep(3) print("over")
執行結果:
父進程 子進程 over linxingzhangdeMacBook-Air:python linxingzhang$ 子進程over over 光標定位到當前位置
全局變量在多個進程中不共享
import os import time g_num = 100 ret = os.fork() if ret == 0: g_num += 1 print("process-c - %d"%g_num) else: time.sleep(3) print("process-p - %d"%g_num)
執行結果:
process-c - 101 process-p - 100
在進程中,全局變量,局部變量,在各自進程的命名空間中,互不干預。
進程和進程之間,數據無法共享。
同一臺電腦進程之間通信:管道,消息隊列...
不同一臺電腦進程之間通信:網絡
多個fork
第一種:多個fork情況,并列fork。
import os # 父進程 ret = os.fork() if ret == 0: # 子進程 print("1") else: # 父進程 print("2") # 父子進程 ret = os.fork() if ret == 0: # 孫子進程 # 2兒子進程 print("11") else: # 兒子進程 # 父進程 print("22")
執行結果:
2 22 1 11 11 22
第二種fork情況,包含fork
import os # 父進程 ret = os.fork() if ret == 0: # 子進程 print("1") else: # 父進程 print("2") ret = os.fork() if ret == 0: # 2兒子進程 print("11") else: # 父進程 print("22")
執行結果:
2 22 1 11
父子進程的執行順序:
父進程、子進程執行順序沒有規律,完全取決于操作系統的調度算法
Process創建子進程
multiprocessing模塊是跨平臺版本的多進程模塊。
# coding=utf-8 from multiprocessing import Process import time def test(): while True: print("--test") time.sleep(2) ret = Process(target=test) ret.start() # 子進程執行代碼 while True: print("--mian") time.sleep(1)
當前執行結果:
--mian --test --mian --test --mian --mian --test --mian --mian ... # 循環
# coding=utf-8 from multiprocessing import Process import os # 子進程執行的代碼 def run_proc(name): print("子進程運行中,name= %s ,pid=%d..." % (name, os.getpid())) if __name__ == "__main__": print("父進程 %d." % os.getpid()) p = Process(target=run_proc, args=("test",)) print("子進程將要執行") p.start() # 子進程開始 p.join() # 等待進程標記結束后才繼續往下走 # 堵塞 print("子進程已結束")
執行結果
父進程 3045. 子進程將要執行 子進程運行中,name= test ,pid=3046... 子進程已結束
創建子進程時,只需要傳入一個執行函數和函數的參數,創建一個Process實例,用start()方法啟動
join()方法可以等待子進程結束后再繼續往下運行,通常用于進程間的同步。
主進程會等待所有的Process子進程先結束,然后再結束主進程。
創建新的進程還能夠使用類的方式,可以自定義一個類,繼承Process類,每次實例化這個類的時候,就等同于實例化一個進程對象
創建新的進程的另一種方式:使用自定義類,繼承Process類,每次實例化當前自定義類的時候,等同與實例話一個進程對象。
from multiprocessing import Process import time class New_Process (Process): # 重寫run方法 def run(nPro): #run print(nPro) # t=while True: print("11") time.sleep(1) p = New_Process() p.start() # 沒有傳遞target參數,會調用run方法 while True: print("main") time.sleep(1)
from multiprocessing import Process import time import os # 繼承Process class Process_Class (Process): # 因為Process類本身也有__init__方法,這個子類相當于重寫了Process的__init__方法,導致,并沒有完全的初始化一個Process類,所以不能子類不能使用繼承的方法和屬性。 # 解決:將繼承類的本身傳遞給Process.__init__方法,完成這些初始化操作。 def __init__(self, interval): Process.__init__(self) self.interval = interval # 重寫Process類的run()方法 def run(self): print("子進程(%s) 開始執行,父進程為(%s)"%(os.getpid(),os.getppid())) t_start = time.time() time.sleep(self.interval) t_stop = time.time() print("(%s)執行結束,耗時%0.2f秒"%(os.getpid(),t_stop-t_start)) if __name__ == "__main__": t_start = time.time() print("當前程序進程(%s)"%os.getpid()) p1 = Process_Class(2) # 實例化 # 對一個不包含target屬性的Process類執行start()方法,就會運行這個類中的run()方法,所以這里會執行p1.run() p1.start() p1.join() t_stop = time.time() print("(%s)執行結束,耗時%0.2f"%(os.getpid(),t_stop-t_start))
Process語法結構
Process([group [, target [, name [, args [, kwargs]]]]])
target: 這個進程實例所調用對象
args: 調用對象的位置參數元組
kwargs: 調用對象的關鍵字參數字典
name: 當前進程實例的別名
Process類常用方法:
is_alive(): 判斷進程實例是否還在執行
join([timeout]): 是否等待進程實例執行結果,或等待多少秒
start(): 創建子進程
run(): 如果沒有給定target參數,對這個對象調用start()方法時,就執行對象中的run()方法
terminate(): 不管任務是否完成,立即終止
Process類常用屬性:
name: 當前進程的實例別名,默認為Process-N, N從1開始遞增的整數。
pid: 當前進程的實例PID值
# coding=utf-8 from multiprocessing import Process import time import os # 兩個子進程將會調用的兩個方法 def worker_1(interval): print("worker_1,父進程(%s),當前進程(%s)"%(os.getppid(),os.getpid())) t_start = time.time() time.sleep(interval) # 程序將會被掛起interval秒 t_end = time.time() print("worker_1,執行時間為"%0.2f"秒"%(t_end - t_start)) def worker_2(interval): print("worker_2,父進程(%s),當前進程(%s)"%(os.getppid(),os.getpid())) t_start = time.time() time.sleep(interval) t_end = time.time() print("worker_2,執行時間為"%0.2f"秒"%(t_end - t_start)) # 輸出當前程序的ID print("進程ID:%s"%os.getpid()) # 創建兩個進程對象,target指向這個進程對象要執行的對象名稱, # args后面的元組中,是要傳遞給worker_1方法的參數, # 因為worker_1方法就一個interval參數,這里傳遞一個整數2給它, # 如果不指定name參數,默認的進程對象名稱為Process-N,N為一個遞增的整數 p1 = Process(target=worker_1,args=(2,)) p2 = Process(target=worker_2,name="alogy",args=(1,)) # 使用"進程對象名稱.start()"來創建并執行一個子進程, # 這兩個進程對象在start后,就會分別去執行worker_1和worker_2方法中的內容 p1.start() p2.start() # 同時父進程仍然往下執行,如果p2進程還在執行,將會返回True print("p2.is_alive=%s"%p2.is_alive()) # 輸出p1和p2進程的別名和pid print("p1.name=%s"%p1.name) print("p1.pid=%s"%p1.pid) print("p2.name=%s"%p2.name) print("p2.pid=%s"%p2.pid) # join括號中不攜帶參數,表示父進程在這個位置要等待p1進程執行完成后, # 再繼續執行下面的語句,一般用于進程間的數據同步,如果不寫這一句, # 下面的is_alive判斷將會是True,在shell(cmd)里面調用這個程序時 # 因為p2需要2秒以上才可能執行完成,父進程等待1秒很可能不能讓p1完全執行完成, # 所以下面的print會輸出True,即p1仍然在執行 p1.join() print("p1.is_alive=%s"%p1.is_alive())
執行結果:
進程ID:14889 p2.is_alive=True p1.name=Process-1 p1.pid=14890 p2.name=alogy p2.pid=14891 worker_1,父進程(14889),當前進程(14890) worker_2,父進程(14889),當前進程(14891) worker_2,執行時間為"1.00"秒 worker_1,執行時間為"2.00"秒 p1.is_alive=False
進程池
池Pool作用:緩沖
進程池優點:增加使用率
創建新的進程的另一種方式:進程池Pool
創建一定數量的進程,然后需要使用的時候拿去使用,使用完畢后歸還。
from multiprocessing import Pool import os import random import time def worker(num): for i in range(random.randint(1, 3)): print("pid = %d, num=%d"%(os.getpid(), num) ) time.sleep(1) p = Pool(3) for i in range(10): p.apply_async(worker,(i )) # 向進程池中添加任務 # 如果添加的任務數超過了進程池中的進程個數的話,那么會導致添加不進入到進程池中。 # 添加到進程池中的任務,如果還沒有被執行的話,那么會等待進程池中的進程完成一個任務之后,會自動去使用已經結束的進程,完成沒有被執行的任務。 p.close() # 關閉進程池,關閉后p實例不再接收新的請求 p.join() # 等待p實例中的所有子進程執行完畢,主進程才會退出, 必須放在close語句之后。
多種方式的比較
os.fork()
Process(target)
Pool
os.fork():
ret = os.fork() if ret == 0: # 子進程 else: # 父進程 # 主進程會立馬結束
Process(target, args):
p1 = Process(atrget=fun) p1.start() p1.join() # 主進程會等待所有子進程都結束
Pool()
pool = Pool(3) pool.apply_asnyc(fun) pool.join() # 主進程在不join()的情況下,會立馬結束,不會等待子進程結束之后再結束主進程。 # 主進程一般都用來等待,任務在子進程中執行。(一般使用進程池)
apply堵塞式添加任務
阻塞式apply()創建多進程
from multiprocessing import Pool def worker(): print(1) p = Pool(3) for i in range(5): p.apply(worker) p.close() p.join()
進程間通信-Queue
Queue本身是一個消息列隊程序
Process方式創建進程需要通過Queue創建通信
進程池創建進程需要通過Manager().Queue()創建通信
#coding=utf-8 from multiprocessing import Queue q = Queue(3) # 初始化一個Queue對象,最多可接收三條put消息 q.put("消息1") q.put("消息2") print(q.full()) # False q.put("消息3") print(q.full()) # True # 因為消息列隊已滿下面的try都會拋出異常,第一個try會等待2秒后再拋出異常,第二個Try會立刻拋出異常 try: q.put("消息4",True,2) except: print("消息列隊已滿,現有消息數量:%s"%q.qsize()) try: q.put_nowait("消息4") except: print("消息列隊已滿,現有消息數量:%s"%q.qsize()) # 先判斷消息列隊是否已滿,再寫入 if not q.full(): q.put_nowait("消息4") # 讀取消息時,先判斷消息列隊是否為空,再讀取 if not q.empty(): for i in range(q.qsize()): print(q.get_nowait())
Queue()語法:
初始化Queue()對象時,(例如:q = Queue()),若參數沒有指定最大可接受消息的數量,或數量為負值,那么就代表可接受的消息數量沒有上限(直到內存的盡頭)
Queue.qsize(): 返回當前隊列包含的消息數量
Queue.empty(): 如果隊列為空,返回True,反之False
Queue.full(): 如果隊列滿了,返回True,反之False
Queue.get_nowait(): 相當于Queue.get(False)
Queue.put_nowait(item): 相當Queue.put(item, False)
Queue.get([block [, timeout]]): 獲取隊列中的一條信息,然后將其隊列中移除,block默認值為True
如果block使用默認值,且沒有設置timeout(單位秒),消息隊列如果為空,此時程序將被阻塞(停在讀取狀態),直到從消息隊列讀到消息為止,如果設置了timeout,則會等待timeout秒,若沒有讀取到任何消息,則拋出Queue.Empty異常。
如果block為False,消息隊列為空,則會立刻拋出Queue.Empty異常
Queue.put(item, [block [, timeout]]): 將item消息寫入隊列,block默認值為True
如果block使用默認值,且沒有設置timeout(單位秒),消息列隊如果已經沒有空間可寫入,此時程序將被阻塞(停在寫入狀態),直到從消息列隊騰出空間為止,如果設置了timeout,則會等待timeout秒,若還沒空間,則拋出Queue.Full異常;
如果block值為False,消息列隊如果沒有空間可寫入,則會立刻拋出Queue.Full異常;
多進程拷貝文件
#coding=utf-8 from multiprocessing import Pool, Manager import os def copy_task (name, old_file, new_file, queue): print(old_file + "/" + name) fr = open(old_file + "/" + name) fw = open(new_file + "/" + name, "w") con = fr.read() fw.write(con) fr.close() fw.close() queue.put(name) def main (): old_file = input("文件夾名字:") new_file = old_file + "_附件" os.mkdir(new_file) file_names = os.listdir(old_file) pool = Pool(5) queue = Manager().Queue() for file in file_names: pool.apply_async(copy_task, args=(file, old_file, new_file, queue)) num = 0 all_num = len(file_names) while num < all_num: queue.get() num += 1 copy_rate = num / all_num print(" copy進度是:%.2f%%"%(copy_rate * 100), end="") print(" 完成copy") if __name__ == "__main__": main()線程
Thread創建多線程
進程:程序運行起來,程序的資源(資源分配的代碼)
線程:進程中的一種,真正執行代碼的東西(CPU調度的代碼)
from threading import Thread import time def test(): print("111") time.sleep(1) for i in range(5): t = Thread(target=test) t.start()
創建好的線程,需要調用start()方法來啟動。
主線程任務結束,不會理解結束,會等待所有子線程結束。
使用Thread子類完成創建多線程
pid: 進程號
tid: 線程號
0號進程:切換進程,處理CPU。(切換進程)
1號進程:間接或直接生成其它進程。
from threading import Thread import time class My_Thread(Thread): def run (self): for i in range(3): time.sleep(1) print(self.name) # name 屬性中保存的是當前線程的名字 if __name__ == "__main__": t = My_Thread() t.start()
線程的執行順序
#coding=utf-8 import threading import time class MyThread(threading.Thread): def run(self): for i in range(3): time.sleep(1) msg = "I"m "+self.name+" @ "+str(i) print(msg) def test(): for i in range(5): t = MyThread() t.start() if __name__ == "__main__": test() # 只能保證每個線程都運行完整個run函數,但是線程的啟動順序、 # run函數中每次循環的執行順序都不能確定。
多線程程序的執行順序是不確定的。當執行到sleep語句時,線程將被阻塞(Blocked),到sleep結束后,線程進入就緒(Runnable)狀態,等待調度。而線程調度將自行選擇一個線程執行。
每個線程一定會有一個名字,盡管上面的例子中沒有指定線程對象的name,但是python會自動為線程指定一個名字。
當線程的run()方法結束時該線程完成。
無法控制線程調度程序,但可以通過別的方式來影響線程調度的方式。
線程的幾種狀態:
線程共享全局變量
from threading import Thread import time g_num = 100 def work1(): global g_num for i in range(3): g_num += 1 print("----in work1, g_num is %d---"%g_num) def work2(): global g_num print("----in work2, g_num is %d---"%g_num) print("---線程創建之前g_num is %d---"%g_num) t1 = Thread(target=work1) t1.start() time.sleep(1) t2 = Thread(target=work2) t2.start()
執行結果:
---線程創建之前g_num is 100--- ----in work1, g_num is 103--- ----in work2, g_num is 103---
在一個進程內的所有線程共享全局變量,能夠在不適用其他方式的前提下完成多線程之間的數據共享。
線程是對全局變量的隨意更改,造成多線程之間的全局變量的混亂(及線程非安全)
全局變量共享的方式(修改):
變量前加global
可變數據類型,例如list
進程和線程的區別
進程:能夠完成多任務,比如:在一臺電腦上能夠同時運行多個QQ
線程:能夠完成多任務,比如:一個QQ中的多個聊天窗口
定義的不同:
進程是系統進行資源分配和調度的一個獨立單位
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計算器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。
區別:
一個程序至少有一個進程,一個進程至少有一個線程.
線程的劃分尺度小于進程(資源比進程少),使得多線程程序的并發性高。
進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率
線線程不能夠獨立執行,必須依存在進程中
優缺點:
線程和進程在使用上各有優缺點:線程執行開銷小,但不利于資源的管理和保護;而進程正相反。
避免全局變量被修改的方式
避免多線程多全局數據影響
輪詢
互斥鎖 (線程同步) 同步就是協同步調,按預定的先后次序進行運行
輪詢:
from threading import Thread import time g_num = 0 g_flag = 1 def test1 (): global g_num global g_flag if g_flag == 1: for i in range(1000000): g_num += 1 g_flag = 0 print("--test--g_num=%d"%g_num) def test2 (): global g_num # 輪詢 while True: if g_flag != 1: for i in range(1000000): g_num += 1 break print("--test2--g_num=%d"%g_num) p1 = Thread(target=test1) p1.start() p2 = Thread(target=test2) p2.start()
互斥鎖
from threading import Thread, Lock import time g_num = 0 def test1 (): global g_num mutex.acquire() for i in range(1000000): g_num += 1 mutex.release() print("--test--g_num=%d"%g_num) def test2 (): global g_num # 輪詢 mutex.acquire() for i in range(1000000): g_num += 1 mutex.release() print("--test2--g_num=%d"%g_num) mutex = Lock() # 互斥鎖,默認是沒有上鎖到 # 一方成功上鎖,那么另一方會堵塞(一直等待)到這個鎖被解開為止 # 一個線程釋放,其它線程都會執行 p1 = Thread(target=test1) p1.start() p2 = Thread(target=test2) p2.start()
# 創建鎖 mutex = threading.Lock() # 鎖定 mutex.acquire([blocking]) # 釋放 mutex.release()
鎖的好處:
確保了某段關鍵代碼只能由一個線程從頭到尾完整地執行
鎖的壞處:
阻止了多線程并發執行,包含鎖的某段代碼實際上只能以單線程模式執行,效率就大大的下降
由于可以存在多個鎖,不同的線程持有不同的鎖,并試圖獲取對方持有的鎖,可能會造成死鎖。
多線程使用非共享變量
僅僅是讀取值,不用調用global
設置值的時候,需要加互斥鎖
在多線程開發中,全局變量是多個線程都共享的數據,而局部變量等是各自線程的,是非共享的(線程中的局部變量,各自不能訪問,各自不影響。)
死鎖
在線程間共享多個資源的時候,如果兩個線程分別占用一部分資源并且同時等待對方的資源,就會造成死鎖。
盡管死鎖很少發生,但一旦發生就會造成應用的停止響應。
死鎖例子:
#coding=utf-8 import threading import time class MyThread1(threading.Thread): def run(self): if mutexA.acquire(): print(self.name+"----do1---up----") time.sleep(1) if mutexB.acquire(): print(self.name+"----do1---down----") mutexB.release() mutexA.release() class MyThread2(threading.Thread): def run(self): if mutexB.acquire(): print(self.name+"----do2---up----") time.sleep(1) if mutexA.acquire(): print(self.name+"----do2---down----") mutexA.release() mutexB.release() mutexA = threading.Lock() mutexB = threading.Lock() if __name__ == "__main__": t1 = MyThread1() t2 = MyThread2() t1.start() t2.start()
避免死鎖
程序設計時要盡量避免(銀行家算法)
添加超時時間: mutexB.acquire(timeout=2)
多線程有序執行
同步就是協調步調,按預定的先后次序進行運行
阻塞,非阻塞:等下執行,還是不等立刻執行。
同步,異步:多方協同執行,是一同執行,還是順序執行。
from threading import Thread,Lock from time import sleep class Task1(Thread): def run(self): while True: if lock1.acquire(): print("------Task 1 -----") sleep(0.5) lock2.release() class Task2(Thread): def run(self): while True: if lock2.acquire(): print("------Task 2 -----") sleep(0.5) lock3.release() class Task3(Thread): def run(self): while True: if lock3.acquire(): print("------Task 3 -----") sleep(0.5) lock1.release() # 使用Lock創建出的鎖默認沒有“鎖上” lock1 = Lock() # 創建另外一把鎖,并且“鎖上” lock2 = Lock() lock2.acquire() # 創建另外一把鎖,并且“鎖上” lock3 = Lock() lock3.acquire() t1 = Task1() t2 = Task2() t3 = Task3() t1.start() t2.start() t3.start()
線程的同步:可以使用互斥鎖完成多個任務,有序的進程工作。
Queue
生產者與消費者模式來解決耦合的問題
Queue:
對于Queue,在多線程通信之間扮演重要的角色(解耦)
添加數據到隊列中,使用put()方法
從隊列中取數據,使用get()方法
判斷隊列中是否還有數據,使用qsize()方法
ThreadLocal對象
ThreadLocal對象在線程中的使用
在多線程環境下,每個線程都有自己的數據。一個線程使用自己的局部變量比使用全局變量好,因為局部變量只有線程自己能看見,不會影響其他線程,而全局變量的修改必須加鎖。
既可以具有各自線程的多帶帶變量,有可以互不影響方法:
使用字典,定義全局變量。
ThreadLocal對象
一個ThreadLocal變量雖然是全局變量,但每個線程都只能讀寫自己線程的獨立副本,互不干擾。ThreadLocal解決了參數在一個線程中各個函數之間互相傳遞的問題
import threading local = threading.local() def process_student(): std = local.student print("Hello, %s (in %s)"%(std, threading.current_thread().name)) def process_thread(name): local.student = name process_student() t1 = threading.Thread(target=process_thread, args=("xixixi",), name="Thread-A") t2 = threading.Thread(target=process_thread, args=("hahaha",), name="Thread-B") t1.start() t2.start() t1.join() t1.join()
ThreadLocal最常用的地方就是為每個線程綁定一個數據庫連接,HTTP請求,用戶身份信息等,這樣一個線程的所有調用到的處理函數都可以非常方便地訪問這些資源。
異步的實現
from multiprocessing import Pool import time import os def test(): print("---進程池中的進程---pid=%d,ppid=%d--"%(os.getpid(),os.getppid())) for i in range(3): print("----%d---"%i) time.sleep(1) return "hhhh" def test2(args): print("---callback func--pid=%d"%os.getpid()) print("---callback func--args=%s"%args) pool = Pool(3) pool.apply_async(func=test,callback=test2) time.sleep(5) print("----主進程-pid=%d----"%os.getpid())
callback主進程返回執行。
子進程返回值到主進程中。
主進程放下當前到任務,去執行其它任務,然后回到執行放下到任務。
GIL問題
全局解釋器鎖(GIL)
Python使用了全局解釋鎖(GIL)的原因,代碼并不能同時在多核上并發的運行,也就是說,Python多線程不能并發。
GIL是多線程間的一把互斥鎖,并且是一把全局鎖,它保證了Cpython在內存管理上面是線程安全的。
原因:
為了發揮多核CPU性能,程序多采用多線程/多進程方式設計。對于多核CPU,操作系統是同時可以啟動多個線程分別在不同的核心上運行,但是由于GIL是關于線程的全局鎖,就可能導致某個任務不停的acquire到GIL,使得其它核心線程停在retry GIL,造成了阻塞的現象。
解決方法:
使用多進程。
GIL是針對線程的鎖,在Python中使用多進程編程。
在Python不合適大量的數學計算,將這些需要大量計算的程序移到C/C++中去實現。
從Python 3.2開始,實現了新的GIL
比之前的GIL增加了一個flag,來控制等待或釋放的狀態
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44545.html
摘要:和是最受歡迎的。虛擬環境將允許將項目依賴項與本地機器依賴項隔離開來。文件將是項目中的主文件。運行后,檢查本地主機。在中創建一個名為的文件夾,并創建一個名為的文件夾。部署創建帳戶為前端和全棧應用程序提供免費部署服務。 ...
摘要:本文最先發布在博客這篇文章將講解并發編程的基本操作。并發是指能夠多任務處理,并行則是是能夠同時多任務處理。雖然自帶了很好的類庫支持多線程進程編程,但眾所周知,因為的存在,很難做好真正的并行。 本文最先發布在博客:https://blog.ihypo.net/151628... 這篇文章將講解 Python 并發編程的基本操作。并發和并行是對孿生兄弟,概念經常混淆。并發是指能夠多任務處...
摘要:本文的分享主要圍繞以下幾個方面能做什么常見應用場景介紹如何學習語法基礎實戰面向對象編程實戰練熟基礎小游戲項目的實現與實戰一能做什么一種編程語言往往可以應用于多方面,有些方面比較常用,有些方面極為常用。比如表示是一個空列表。 摘要:Python語言的教程雖然隨處可見,但是忙于日常業務/學習的你或許:一直想要找個時間學一點,但是又不知道該從何下手?本文將從Python能做什么,如何學習Py...
摘要:一直都挺喜歡這個社區的,給人的第一感覺就是比較的專業正式,社區內氛圍不錯,各種文章的質量也很好,并且幫助了我很多。很開心能夠來到這里,記錄自己的成長,希望自己能夠多活躍一下,無論是在問答上面還是寫作上面。 一直都挺喜歡 Segmentfault 這個社區的,給人的第一感覺就是比較的專業正式,社區內氛圍不錯,各種文章的質量也很好,并且幫助了我很多。很開心能夠來到這里,記錄自己的成長,希望...
閱讀 2434·2021-11-15 11:36
閱讀 1172·2019-08-30 15:56
閱讀 2243·2019-08-30 15:53
閱讀 1038·2019-08-30 15:44
閱讀 649·2019-08-30 14:13
閱讀 997·2019-08-30 10:58
閱讀 476·2019-08-29 15:35
閱讀 1293·2019-08-29 13:58