一、線程
線程顧名思義,就是一條流水線工作的過程,一條流水線必須屬于一個(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í)例對(duì)象的方法:
- isAlive(): 返回線程是否活動(dòng)的。
- getName(): 返回線程名。
- setName(): 設(shè)置線程名。
threading模塊提供的一些方法:
- threading.currentThread(): 返回當(dāng)前的線程變量。
- threading.enumerate(): 返回一個(gè)包含正在運(yùn)行的線程的list。正在運(yùn)行指線程啟動(dòng)后、結(jié)束前,不包括啟動(dòng)前和終止后的線程。
- threading.activeCount(): 返回正在運(yùn)行的線程數(shù)量,與len(threading.enumerate())有相同的結(jié)果。
四、守護(hù)線程
無論是進(jìn)程還是線程,都遵循:守護(hù)xxx會(huì)等待主xxx運(yùn)行完畢后被銷毀
需要強(qiáng)調(diào)的是:運(yùn)行完畢并非終止運(yùn)行
1.對(duì)主進(jìn)程來說,運(yùn)行完畢指的是主進(jìn)程代碼運(yùn)行完畢
2.對(duì)主線程來說,運(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)程會(huì)一直等非守護(hù)的子進(jìn)程都運(yùn)行完畢后回收子進(jìn)程的資源(否則會(huì)產(chǎn)生僵尸進(jìn)程),才會(huì)結(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)勢(shì)。
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)勢(shì))
如果是io密集型操作:開多線程
如果是計(jì)算密集型:開多進(jìn)程