一、線程
線程顧名思義冬三,就是一條流水線工作的過程,一條流水線必須屬于一個(gè)車間缘缚,一個(gè)車間的工作過程是一個(gè)進(jìn)程
車間負(fù)責(zé)把資源整合到一起勾笆,是一個(gè)資源單位,而一個(gè)車間內(nèi)至少有一個(gè)流水線
流水線的工作需要電源桥滨,電源就相當(dāng)于cpu
所以窝爪,進(jìn)程只是用來把資源集中到一起(進(jìn)程只是一個(gè)資源單位弛车,或者說資源集合),而線程才是cpu上的執(zhí)行單位蒲每。
多線程(即多個(gè)控制線程)的概念是纷跛,在一個(gè)進(jìn)程中存在多個(gè)控制線程,多個(gè)控制線程共享該進(jìn)程的地址空間邀杏,相當(dāng)于一個(gè)車間內(nèi)有多條流水線贫奠,都共用一個(gè)車間的資源。
進(jìn)程是資源分配的最小單位望蜡,線程是CPU調(diào)度的最小單位唤崭。每一個(gè)進(jìn)程中至少有一個(gè)線程。
二脖律、開啟線程的兩種方式
method 1 :
from threading import Thread
import time
def task(n):
print(f"線程{n}開始")
time.sleep(2)
print(f"線程{n}結(jié)束")
if __name__ == '__main__':
t = Thread(target=task,args=(1,))
t.start()
t2 = Thread(target=task, args=(2,))
t2.start()
print("主")
method 2 :
from threading import Thread
import time
class MyThread(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self) -> None:
print(f"進(jìn)程{self.name}開始")
time.sleep(2)
print(f"進(jìn)程{self.name}結(jié)束")
if __name__ == '__main__':
t = MyThread("1")
t.start()
t1 = MyThread("2")
t1.start()
t.join()
t1.join()
print("主")
三谢肾、統(tǒng)一進(jìn)程下多線程數(shù)據(jù)共享
from threading import Thread
import time
money = 99
def task(n):
global money
money=n
print('開始')
# time.sleep(n)
print('結(jié)束')
if __name__ == '__main__':
t = Thread(target=task, args=(2,))
t.start()
t1 = Thread(target=task, args=(66,))
t1.start()
t.join()
t1.join()
print(money)
print('主')
線程相關(guān)的其他方法:
Thread實(shí)例對象的方法:
- isAlive(): 返回線程是否活動的。
- getName(): 返回線程名状您。
- setName(): 設(shè)置線程名勒叠。
threading模塊提供的一些方法:
- threading.currentThread(): 返回當(dāng)前的線程變量。
- threading.enumerate(): 返回一個(gè)包含正在運(yùn)行的線程的list膏孟。正在運(yùn)行指線程啟動后眯分、結(jié)束前,不包括啟動前和終止后的線程柒桑。
- threading.activeCount(): 返回正在運(yùn)行的線程數(shù)量弊决,與len(threading.enumerate())有相同的結(jié)果。
四魁淳、守護(hù)線程
無論是進(jìn)程還是線程飘诗,都遵循:守護(hù)xxx會等待主xxx運(yùn)行完畢后被銷毀
需要強(qiáng)調(diào)的是:運(yùn)行完畢并非終止運(yùn)行
1.對主進(jìn)程來說,運(yùn)行完畢指的是主進(jìn)程代碼運(yùn)行完畢
2.對主線程來說界逛,運(yùn)行完畢指的是主線程所在的進(jìn)程內(nèi)所有非守護(hù)線程統(tǒng)統(tǒng)運(yùn)行完畢昆稿,主線程才算運(yùn)行完畢
詳細(xì)解釋:
1 主進(jìn)程在其代碼結(jié)束后就已經(jīng)算運(yùn)行完畢了(守護(hù)進(jìn)程在此時(shí)就被回收),然后主進(jìn)程會一直等非守護(hù)的子進(jìn)程都運(yùn)行完畢后回收子進(jìn)程的資源(否則會產(chǎn)生僵尸進(jìn)程),才會結(jié)束息拜。
2 主線程在其他非守護(hù)線程運(yùn)行完畢后才算運(yùn)行完畢(守護(hù)線程在此時(shí)就被回收)溉潭。因?yàn)橹骶€程的結(jié)束意味著進(jìn)程的結(jié)束,進(jìn)程整體的資源都將被回收少欺,而進(jìn)程必須保證非守護(hù)線程都運(yùn)行完畢后才能結(jié)束喳瓣。
五、GIL全局解釋器鎖
在Cpython解釋器中赞别,同一個(gè)進(jìn)程下開啟的多線程畏陕,同一時(shí)刻只能有一個(gè)線程執(zhí)行,無法利用多核優(yōu)勢仿滔。
python的解釋器有很多惠毁,cpython犹芹,jpython,pypy(python寫的解釋器)
python的庫多仁讨,庫都是基于cpython寫起來的羽莺,其他解釋器沒有那么多的庫
cpython中有一個(gè)全局大鎖实昨,每條線程要執(zhí)行洞豁,必須獲取到這個(gè)鎖
這個(gè)鎖存在的原因是因?yàn)閜ython的垃圾回收機(jī)制
python的多線程其實(shí)就是單線程
某個(gè)線程想要執(zhí)行,必須先拿到GIL荒给,我們可以把GIL看作是“通行證”丈挟,并且在一個(gè)python進(jìn)程中,GIL只有一個(gè)志电。拿不到通行證的線程曙咽,就不允許進(jìn)入CPU執(zhí)行
總結(jié):cpython解釋器中有一個(gè)全局鎖(GIL),線程必須獲取到GIL才能執(zhí)行挑辆,我們開的多線程例朱,不管有幾個(gè)cpu,同一時(shí)刻鱼蝉,只有一個(gè)線程在執(zhí)行(python的多線程洒嗤,不能利用多核優(yōu)勢)
如果是io密集型操作:開多線程
如果是計(jì)算密集型:開多進(jìn)程