在討論進(jìn)程和線程之前引瀑,我們先討論一下cpu的核心數(shù)的概念
那我的電腦舉例,cpu的型號是:i5 3317u榨馁,這是一顆雙核心四線程的處理器憨栽。一般來說,都是一個(gè)核心翼虫,運(yùn)行一個(gè)線程屑柔,那為什么這個(gè)兩核的處理器,可以運(yùn)行四個(gè)線程蛙讥?
找到一個(gè)解釋:
現(xiàn)在的cpu都利用特殊的硬件指令锯蛀,把兩個(gè)物理內(nèi)核模擬為四個(gè)邏輯內(nèi)核,讓單個(gè)處理器都能使用線程級并行計(jì)算次慢。進(jìn)而兼容多線程操作系統(tǒng)和軟件,減少了CPU的閑置時(shí)間翔曲,提高的CPU的運(yùn)行效率迫像。
也就是說,從硬件的角度來看瞳遍,他是兩個(gè)核心闻妓,從操作系統(tǒng)的角度看,他是四個(gè)核心掠械。
當(dāng)然這一點(diǎn)也很方便的可以從操作系統(tǒng)的層面上證實(shí):
打開任務(wù)管理器>性能>右鍵 將圖形更改為 >邏輯處理器
通過python也可以的到由缆,機(jī)器的核心數(shù)。
這個(gè)要import multiprocessing
這是我的虛擬機(jī)猾蒂,只給它分配了一個(gè)核均唉。
現(xiàn)在我們來討論進(jìn)程和線程
進(jìn)程
那么對于計(jì)算量比較大的程序,我們可以使用多進(jìn)程的模型來開發(fā)肚菠。多個(gè)進(jìn)程分別運(yùn)行在cpu的多個(gè)核上面舔箭,可以成倍的提高效率。但如果同時(shí)運(yùn)行進(jìn)程大于CPU核心數(shù)蚊逢,則至少有個(gè)核心要同時(shí)運(yùn)行2個(gè)或以上的任務(wù)层扶,這樣的并發(fā)執(zhí)行中會帶來任務(wù)的切換開銷,降低效率烙荷。
線程
因?yàn)镻ython的線程雖然是真正的線程镜会,但解釋器執(zhí)行代碼時(shí),有一個(gè)GIL鎖:Global Interpreter Lock终抽,任何Python線程執(zhí)行前戳表,必須先獲得GIL鎖焰薄,然后,每執(zhí)行100條字節(jié)碼扒袖,解釋器就自動釋放GIL鎖塞茅,讓別的線程有機(jī)會執(zhí)行。這個(gè)GIL全局鎖實(shí)際上把所有線程的執(zhí)行代碼都給上了鎖季率,所以野瘦,多線程在Python中只能交替執(zhí)行,即使100個(gè)線程跑在100核CPU上飒泻,也只能用到1個(gè)核鞭光。
GIL是Python解釋器設(shè)計(jì)的歷史遺留問題,通常我們用的解釋器是官方實(shí)現(xiàn)的CPython泞遗,要真正利用多核惰许,除非重寫一個(gè)不帶GIL的解釋器。
所以史辙,在Python中汹买,可以使用多線程,但不要指望能有效利用多核聊倔。如果一定要通過多線程利用多核晦毙,那只能通過C擴(kuò)展來實(shí)現(xiàn),不過這樣就失去了Python簡單易用的特點(diǎn)耙蔑。
不過见妒,也不用過于擔(dān)心,Python雖然不能利用多線程實(shí)現(xiàn)多核任務(wù)甸陌,但可以通過多進(jìn)程實(shí)現(xiàn)多核任務(wù)须揣。多個(gè)Python進(jìn)程有各自獨(dú)立的GIL鎖,互不影響钱豁。
所以耻卡,多線程(cpu密集型的任務(wù))在python下面其實(shí)很雞肋,因?yàn)闊o論創(chuàng)建多少線程寥院,這些線程只會運(yùn)行在一個(gè)核心上面劲赠。
舉一個(gè)例子
#對于一個(gè)cpu密集型的任務(wù),計(jì)算斐波那契數(shù)列秸谢。
#類別一:分別用兩個(gè)線程 凛澎,每一個(gè)各自計(jì)算一個(gè)fib(35)
#類別二:用一個(gè)單線程,計(jì)算兩次fib(35)
#比較一下 他們兩個(gè)誰更快估蹄?
import time
import threading
def profile(func):
def wrapper(* args ,**kwargs):
start =time.time()
func(*args,**kwargs)
end =time.time()
print ("COST : {}".format(end-start))
return wrapper
def fib(n):
if n <=2:
return 1
else:
return fib(n-1)+fib(n-2)
@profile
def has_thread():
threadlist=[]
threadlist.append(threading.Thread(target=fib,args=(35,)))
threadlist.append(threading.Thread(target=fib,args=(35,)))
for i in threadlist:
i.start()
for i in threadlist:
i.join()
@profile
def no_thread():
fib(35)
fib(35)
if __name__ == "__main__":
has_thread()
no_thread()
結(jié)果是塑煎,單線程比多線程還快:
原因是多線程以為gil的限制,并沒有辦法真正的并行臭蚁,只是交替的占用cpu最铁,同時(shí)在加上線程切換的開銷讯赏。導(dǎo)致結(jié)果比單線程還差。