一、什么是線(xiàn)程?
線(xiàn)程顧名思義,就是一條流水線(xiàn)工作的過(guò)程,一條流水線(xiàn)必須屬于一個(gè)車(chē)間,一個(gè)車(chē)間的工作過(guò)程是一個(gè)進(jìn)程。車(chē)間負(fù)責(zé)把資源整合到一起,是一個(gè)資源單位,而一個(gè)車(chē)間內(nèi)至少有一個(gè)流水線(xiàn)。所以,進(jìn)程只是用來(lái)把資源集中到一起(進(jìn)程只是一個(gè)資源單位,或者說(shuō)資源集合),而線(xiàn)程才是cpu上的執(zhí)行單位。
總結(jié)進(jìn)程與線(xiàn)程區(qū)別:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
''' 進(jìn)程:資源單位 線(xiàn)程:執(zhí)行單位 線(xiàn)程才是真正干活的人,干活中需要的資源由線(xiàn)程所在進(jìn)程提供 每個(gè)進(jìn)程肯定自帶一個(gè)線(xiàn)程 每個(gè)進(jìn)程內(nèi)可創(chuàng)建多個(gè)線(xiàn)程 ''' ''' 開(kāi)進(jìn)程: 申請(qǐng)空間 拷貝代碼 消耗資源大 開(kāi)線(xiàn)程: 同一個(gè)進(jìn)程內(nèi)創(chuàng)建多個(gè)線(xiàn)程,無(wú)需上述兩種操作,消耗資源相對(duì)較小 ''' |
多線(xiàn)程(即多個(gè)控制線(xiàn)程)的概念是,在一個(gè)進(jìn)程中存在多個(gè)控制線(xiàn)程,多個(gè)控制線(xiàn)程共享該進(jìn)程的地址空間,相當(dāng)于一個(gè)車(chē)間內(nèi)有多條流水線(xiàn),都共用一個(gè)車(chē)間的資源。
二、開(kāi)啟線(xiàn)程的兩種方式
1、方式1
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from threading import Thread import time # 方法一 def task(name): print ( '%s is running' % name) time.sleep( 1 ) print ( '%s is over' % name) # 開(kāi)啟線(xiàn)程不需要在main下面執(zhí)行代碼,直接書(shū)寫(xiě)就可以 # 但是習(xí)慣性的將啟動(dòng)命令寫(xiě)在main下面 t = Thread(target = task, args = ( 'egon' ,)) t.start() # 創(chuàng)建線(xiàn)程的開(kāi)銷(xiāo)非常小,幾乎是代碼一執(zhí)行就已經(jīng)創(chuàng)建了 print ( '主' ) ''' |
運(yùn)行結(jié)果:
egon is running
主
egon is over
'''
2、方式2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from threading import Thread class MyThread(Thread): def __init__( self , name): # 重寫(xiě)了別人的方法,又不知道別人的方法里有啥,就調(diào)用父類(lèi)的方法 super ().__init__() self .name = name def run( self ): print ( '%s is running' % self .name) time.sleep( 1 ) print ( '%s is over' % self .name) if __name__ = = '__main__' : t = MyThread( 'egon' ) t.start() print ( '主' ) ''' |
運(yùn)行結(jié)果:
egon is running
主
egon is over
'''
三、線(xiàn)程對(duì)象的jion方法()
看過(guò)我講解進(jìn)程文章的小伙伴想必都知道jion的功能,線(xiàn)程的jion方法于進(jìn)程的jion方法功能類(lèi)似-等待一個(gè)線(xiàn)程執(zhí)行完畢后再執(zhí)行下一個(gè)線(xiàn)程
1
2
3
4
5
6
7
8
9
10
|
from threading import Thread def task(name): print ( '%s is running' % name) time.sleep( 1 ) print ( '%s is over' % name) if __name__ = = '__main__' : t = Thread(target = task,args = ( 'egon' ,)) t.start() t.join() # 主線(xiàn)程等待子線(xiàn)程運(yùn)行結(jié)束后再執(zhí)行 print ( '主' ) |
?'''
運(yùn)行結(jié)果:
egon is running
egon is over
主
'''
補(bǔ)充一個(gè)知識(shí)點(diǎn):同一個(gè)進(jìn)程下的多個(gè)線(xiàn)程數(shù)據(jù)共享,下面為大家舉一個(gè)簡(jiǎn)單的案例
1
2
3
4
5
6
7
8
9
|
from threading import Thread money = 100 def task(): global money money = 66 if __name__ = = '__main__' : t = Thread(target = task,args = ()) t.start() print (money) |
# 結(jié)果:66
四、 補(bǔ)充小案例
1
2
3
4
5
6
7
8
9
|
from threading import Thread import os,time def task(): print ( '子 pid:' ,os.getpid()) if __name__ = = '__main__' : t = Thread(target = task,args = ()) t.start() print ( '主 pid:' ,os.getpid()) # 兩個(gè)線(xiàn)程的pid號(hào)一樣,說(shuō)明在同一個(gè)進(jìn)程下 |
'''
運(yùn)行結(jié)果:
子 pid: 13444
主 pid: 13444
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 這是個(gè)容易混淆的案例 from threading import Thread,current_thread,active_count import os,time def task(n): print ( '子' ,current_thread().name) time.sleep(n) # 延長(zhǎng)線(xiàn)程存活時(shí)間 if __name__ = = '__main__' : t = Thread(target = task,args = ( 1 ,)) t1 = Thread(target = task,args = ( 1 ,)) t.start() t1.start() t.join() # print('主',current_thread().name)# 獲取線(xiàn)程名字 print (active_count()) # 統(tǒng)計(jì)當(dāng)前活躍的進(jìn)程數(shù) |
'''
運(yùn)行結(jié)果:
子 Thread-1
子 Thread-2
1
'''
# 這里大家容易以為是3,其實(shí)運(yùn)行后只有一個(gè)線(xiàn)程在活躍了,其它兩個(gè)線(xiàn)程運(yùn)行完后就停止運(yùn)行了
五、守護(hù)線(xiàn)程
守護(hù)線(xiàn)程與守護(hù)進(jìn)程的概念也類(lèi)似,其實(shí)大家也能注意到,進(jìn)程與線(xiàn)程有許多知識(shí)點(diǎn)即用法都是相通的,理解了一個(gè)另一個(gè)也是差不多的道理
1、守護(hù)線(xiàn)程會(huì)隨著主線(xiàn)程的結(jié)束而結(jié)束
2、主線(xiàn)程運(yùn)行結(jié)束后不會(huì)立刻結(jié)束,會(huì)等待所有的其它非守護(hù)線(xiàn)程結(jié)束后才會(huì)結(jié)束
3、因?yàn)橹骶€(xiàn)程的結(jié)束意味著所在進(jìn)程的結(jié)束
1
2
3
4
5
6
7
8
9
10
11
|
from threading import Thread import time def task(name): print ( '%s is running' % name) time.sleep( 1 ) print ( '%s is over' % name) if __name__ = = '__main__' : t = Thread(target = task,args = ( 'egon' ,)) t.daemon = True #將t設(shè)置為守護(hù)線(xiàn)程 t.start() print ( '主' ) |
'''
運(yùn)行結(jié)果:
egon is running
主
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 稍微有點(diǎn)迷惑性的例子 from threading import Thread import time def foo(): print ( '1234' ) time.sleep( 1 ) print ( 'end1234' ) def func(): print ( '5678' ) time.sleep( 3 ) print ( 'end5678' ) if __name__ = = '__main__' : t1 = Thread(target = foo,args = ()) t2 = Thread(target = func,args = ()) t1.daemon = True # t1設(shè)為守護(hù)線(xiàn)程,t2為非守護(hù)線(xiàn)程 t1.start() t2.start() print ( '主......' ) |
'''
運(yùn)行結(jié)果:
1234
5678主......
end1234
end5678
'''
'''
因主線(xiàn)程會(huì)等待非守護(hù)線(xiàn)程運(yùn)行結(jié)束后在結(jié)束,
所有主線(xiàn)程會(huì)等待t2(非守護(hù)線(xiàn)程)結(jié)束再結(jié)束,
'''
六、線(xiàn)程互斥鎖
多個(gè)線(xiàn)程操作同一份數(shù)據(jù)的時(shí)候,會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂的問(wèn)題
針對(duì)上述問(wèn)題,解決方式就是加鎖處理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
from threading import Thread,Lock import time money = 100 mutex = Lock() def task(): global money mutex.acquire() tmp = money time.sleep( 0.1 ) # 模擬網(wǎng)絡(luò)延遲 money = tmp - 1 mutex.release() if __name__ = = '__main__' : t_list = [] for i in range ( 100 ): t = Thread(target = task,args = ()) t.start() t_list.append(t) for t in t_list: t.join() print (money) |
# 運(yùn)行結(jié)果:0
# 多個(gè)人操作同一份數(shù)據(jù),數(shù)據(jù)錯(cuò)亂,加鎖處理
七、GTL-全局解釋器
相信學(xué)python的小伙伴都知道,python解釋器其實(shí)有多個(gè)版本
- Cpython
- Jpython
- Pypython
但是普遍使用的都是Cpython解釋器
在Cpython解釋器中GIL是一把互斥鎖,用來(lái)阻止同一個(gè)進(jìn)程下的多個(gè)線(xiàn)程的同時(shí)執(zhí)行
要注意同一進(jìn)程下的多個(gè)線(xiàn)程無(wú)法利用多核優(yōu)勢(shì)!!!!
想必大家心中也有不少疑惑:pyhon的多線(xiàn)程是不是一點(diǎn)用都沒(méi)了呢????
因?yàn)镃python中的內(nèi)存管理不是線(xiàn)程安全的。多線(xiàn)程并不是一無(wú)是處的,在遇到多IO操作的時(shí)候,多核的優(yōu)勢(shì)也會(huì)顯示不出來(lái),多進(jìn)程與多線(xiàn)程的效率在該情況下差不了多少,而此時(shí)多進(jìn)程相對(duì)浪費(fèi)資源,多線(xiàn)程更加節(jié)省資源
ps:內(nèi)存管理就是垃圾回收機(jī)制:
1、引用計(jì)數(shù)
2、標(biāo)記清除
3、分帶回收
1
2
3
4
5
6
|
# GTL-全局解釋器 # 重點(diǎn):1、GIL不是python的特點(diǎn)而是Cpython解釋器的特點(diǎn) # 2、GIL是保證解釋器級(jí)別的數(shù)據(jù)的安全 # 3、GIL會(huì)導(dǎo)致同一個(gè)進(jìn)程下的多個(gè)線(xiàn)程無(wú)法同時(shí)進(jìn)行(即無(wú)法利用多核優(yōu)勢(shì)) # 4、針對(duì)不同的數(shù)據(jù)還是需要加不同的鎖處理 # 5、解釋型語(yǔ)言的通病,同一個(gè)進(jìn)程下多個(gè)線(xiàn)程無(wú)法利用多核優(yōu)勢(shì) |
多線(xiàn)程是否有用要看具體情況
八、驗(yàn)證多線(xiàn)程與多線(xiàn)程運(yùn)用場(chǎng)景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 計(jì)算密集型(CPU一直工作,也沒(méi)有IO)(更適合多進(jìn)程) from multiprocessing import Process from threading import Thread import os,time # 多進(jìn)程情況 def work(): res = 0 for i in range ( 0 , 10000000 ): res * = i if __name__ = = '__main__' : l = [] print (os.cpu_count()) # 獲取當(dāng)前計(jì)算機(jī)CPU核數(shù) start_time = time.time() for i in range ( 8 ): # 我計(jì)算機(jī)是8核 p = Process(target = work,args = ()) p.start() l.append(p) for p in l: p.join() print (time.time() - start_time) |
'''
運(yùn)行結(jié)果:
8
2.0726492404937744
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 多線(xiàn)程情況 from multiprocessing import Process from threading import Thread import os,time def work(): res = 0 for i in range ( 0 , 10000000 ): res * = i if __name__ = = '__main__' : l = [] print (os.cpu_count()) # 獲取當(dāng)前計(jì)算機(jī)CPU核數(shù) start_time = time.time() for i in range ( 8 ): # 我計(jì)算機(jī)是8核 t = Thread(target = work,args = ()) t.start() l.append(t) for p in l: p.join() print (time.time() - start_time) |
'''
運(yùn)行結(jié)果:
8
3.5790603160858154
'''
# 顯然可知:計(jì)算密集型更時(shí)候多進(jìn)程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# IO密集型(任務(wù)一直有IO)(多線(xiàn)程更合適) from multiprocessing import Process from threading import Thread import os,time # 多線(xiàn)程 def work(): time.sleep( 1 ) if __name__ = = '__main__' : l = [] start_time = time.time() for i in range ( 40 ): t = Thread(target = work,args = ()) t.start() l.append(t) for p in l: p.join() print (time.time() - start_time) # 運(yùn)行結(jié)果:1.0205152034759521 # 多進(jìn)程 from multiprocessing import Process from threading import Thread import os,time def work(): time.sleep( 1 ) if __name__ = = '__main__' : l = [] start_time = time.time() for i in range ( 40 ): p = Process(target = work,args = ()) # t=Thread(target=work,args=()) # t.start() # l.append(t) p.start() l.append(p) for p in l: p.join() print (time.time() - start_time) |
# 運(yùn)行結(jié)果:5.927189588546753
# 顯然可知:IO密集型更適合多線(xiàn)程
總結(jié):
多線(xiàn)程和多進(jìn)程都各自有各自的優(yōu)勢(shì)
并且在后面的項(xiàng)目中通常可以多進(jìn)程下面再開(kāi)設(shè)多線(xiàn)程
這樣的話(huà)我們可以利用多核也可以節(jié)省資源消耗
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注服務(wù)器之家的更多內(nèi)容!
原文鏈接:https://blog.csdn.net/m0_51734025/article/details/121597348