python基礎(chǔ)語法(15.5)
多線程
初步了解多線程
(個人理解)電腦的cpu一般情況下一次只能給一個進程提供服務(wù),它給每個進程提供服務(wù)的時間是相同的,到達時間后就會給下一個進程提供服務(wù),這種行為被稱之為時間片輪轉(zhuǎn)。但是由于cpu的速度實在太快了,所以造成看似好像是所有程序一起并發(fā)執(zhí)行。
但是有些時候,有些程序較為耗費資源,一般cpu輪轉(zhuǎn)提供給它的服務(wù)不足以支撐它運行。這時候他就會開多天線程,cpu會輪轉(zhuǎn)給每個線程提供單位時間的服務(wù)。如此一來就可以讓該程序獲得更多的資源。
線程是應(yīng)用程序中工作的最小單位。Python當(dāng)前的多線程庫沒有實現(xiàn)優(yōu)先級,線程組,線程也不能被停止,暫停,恢復(fù),中斷。
多線程具有以下有點:
- 使用線程可以把程序中長時間占用資源的任務(wù)放到后臺處理。
- 可以讓顯示界面更加靈活,通過點擊等事件去觸發(fā)一些線程,并且可以在線程進行的過程中給用戶相應(yīng)的反饋。
- 提升程序運行速度。
- 一些不需要連續(xù)使用系統(tǒng)資源的任務(wù),系統(tǒng)可以在任務(wù)間歇將CPU分配給其它進程,更好的利用系統(tǒng)資源。
如何使用多線程
Python的標(biāo)準(zhǔn)庫給我們提供了兩個標(biāo)準(zhǔn)模塊:_thread 和threading。
_thread是低級模塊,threading是高級模塊,它對 _thread 進行了封裝,我們常用threading模塊。
threading提供的類
Thread
該類可以按一下兩種方式安全的進行子類化:
- 通過將可調(diào)用對象傳遞給構(gòu)造函數(shù)。
- 通過重寫子類中的run()方法。
這是一個構(gòu)造函數(shù),調(diào)用它需要傳入關(guān)鍵字參數(shù),其中
? *group默認為空,為ThreadGroup將來擴展保留的。
? *target是run()調(diào)用的可調(diào)用的對象方法。默認為none,表示不調(diào)用任何內(nèi)容。
? *name是線程名稱,默認情況下,構(gòu)造函數(shù)的唯一名稱,形式為“Thread-N”,其中N是小十進制數(shù)。
? *args是目標(biāo)調(diào)用的參數(shù)元組,默認為()
? *kwargs是目標(biāo)關(guān)鍵字參數(shù)的字典調(diào)用,默認為{}
? 如果子類重寫構(gòu)造函數(shù),則必須確保調(diào)用積累構(gòu)造函數(shù)(Thread_init_())
Lock
Rlock
Condition
Event
Timer
local
[Bounded]Semaphore
實例方法
isAlive():返回線程是否在運行,在啟動后,終止前都稱之為運行。
get/setName(name):獲取/設(shè)置線程名
start()“線程準(zhǔn)備就緒,等待CPU調(diào)度
-
join([timeout]):阻塞當(dāng)前的上下文環(huán)境的線程,直到調(diào)用此方法的線程終止或達到指定的timeout(可選參數(shù))
- 上下文
每個線程都有屬于它的一組CPU寄存器,稱之為線程的上下文,上下文反應(yīng)了線程上次運行該線程的CPU寄存器狀態(tài)。
指令指針和堆棧指針寄存器是線程上下文中兩個重要的寄存器,線程總是在進程得到的上下文中運行的,這些地址都用于標(biāo)注擁有線程的進程地址空間中的內(nèi)存。
threading模塊提供的常量
在threading.TIMEOUT_MAX設(shè)置threading全局超時時間。
以函數(shù)的形式來開啟線程
import threading
import time
#將要執(zhí)行的方法作為參數(shù)傳遞給Thread的構(gòu)造方法
def action(arg):
time.sleep(1)
print(threading.current_thread()) # 返回當(dāng)前的線程對象
print("The arg is:{0}".format(arg))#注意是format不是formate
for i in range(4):
t = threading.Thread(target = action,args = (i,)) # 創(chuàng)建調(diào)用action的線程
t.start() # 就緒線程,會自動執(zhí)行run()來運行線程
if __name__ == "__main__":
print(threading.current_thread())
print("main thread End")
運行結(jié)果:
<_MainThread(MainThread, started 24928)>
main thread End
<Thread(Thread-1, started 15260)>
The arg is :0
<Thread(Thread-2, started 18576)>
<Thread(Thread-3, started 19916)>
The arg is :2
The arg is :1
<Thread(Thread-4, started 296)>
The arg is :3
用類來包裝線程對象
即自己定義一個類來繼承Thread,通過重寫run()方法來開啟多線程。
代碼實例:
class MyThread(threading.Thread):
def __init__(self,arg):
super(MyThread,self).__init__() #調(diào)用父類
self.arg = arg #初始化傳入?yún)?shù)
def run(self): #重寫該方法,定義每個線程要運行的函數(shù)
time.sleep(1)
print("The arg is :{0}".format(self.arg))
for i in range(4):
t = MyThread(i)
t.start()
運行結(jié)果:
The arg is :0
The arg is :2
The arg is :1
The arg is :3
threading模塊提供的常用方法:
- threading.currentThread():返回當(dāng)前的線程變量。
- threading.enumerate():返回一個包含正在運行的線程的list。(正在運行的線程是指線程啟動后,結(jié)束前,不包括啟動前和終止后的線程。)
- threading.active Count():返回正在運行的線程數(shù)量,與len(threading.enumerate())有相同的結(jié)果。
后臺線程與前臺線程
is/setDeamon(bool):獲取/設(shè)置是否為后臺線程(默認為前臺線程(False))。
注: 在start()之前設(shè)置
區(qū)別:
- 后臺線程,主線程結(jié)束,后臺線程立刻停止。
- 前臺線程,主線程結(jié)束,等待前臺線程執(zhí)行完程序停止。
相同點:
? 不論是前臺還是后臺,在主線程運行時它們也運行。
代碼實例:
if __name__ == '__main__':
for i in range(4):
t = MyThread(i)
t.setDaemon(True)
t.start()
print('main thread end!')
運行結(jié)果:
main thread end!
如果設(shè)置為前臺線程,運行結(jié)果為:
main thread end!
The arg is :0
The arg is :1
The arg is :3
The arg is :2
join() 線程阻塞
該方法會阻塞當(dāng)前上下文環(huán)境線程,直到調(diào)用此方法的線程終止或者到達指定的timeout。即使設(shè)置了setDeamon(True)主線程依舊要等待子線程結(jié)束。
** 線程必須先start()然后再join()**
代碼實例:
if __name__ == '__main__':
th=[]
for i in range(100):
t = MyThread(i)
th.append(t)
t.start()
```
不能直接寫成t.join(),這樣只會阻塞主線程一次,無法確保再阻塞過程中線程都執(zhí)行完。
下面的for循環(huán)時要阻塞主線程,創(chuàng)建的線程數(shù)那么多次,可以保證主線程最后執(zhí)行完。
這種不同于再上面的for循環(huán)里寫join(),寫在上面會使每個線程不光阻塞主線程,也阻塞接下來的線程。時多線程無意義。
```
for tt in th:
tt.join()
#設(shè)置join之后,主線程等待子線程全部執(zhí)行完成后或者子線程超時后,主線程才結(jié)束
print('main thread end!')