日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python线程(二)代码部分Threading模块

發布時間:2023/12/20 python 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python线程(二)代码部分Threading模块 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、Thread類簡介

1、Thread類參數簡介

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
group:目前此參數為None,在實現ThreadGroup類時為將來的擴展保留。
target:target接收的是一個函數的地址,由run()方法調用執行函數中的內容。默認為無,表示未調用任何內容。
name :線程名,可自行定義。

args:target接收的是函數名,此函數的位置參數以元組的形式存放在args中,用于執行函數時調用。
kwargs :target接收的是函數名,此函數的關鍵字參數以字典的形式存放在kwargs中,用于執行函數時調用

daemon:如果為True表示該線程為守護線程。

2、Thread類的常用方法

start():開啟線程,一個Thread對象只能調用一次start()方法,如果在同一線程對象上多次調用此方法,則會引發RuntimeError。

run():執行start()方法會調用run(),該方將創建Thread對象時傳遞給target的函數名,和傳遞給args、kwargs的參數組合成一個完整的函數,并執行該函數。run()方法一般在自定義Thead類時會用到。

join(timeout=None):join會阻塞、等待線程,timeout單位為秒,因為join()總是返回none,所以在設置timeout調用join(timeout)之后,需要使用isalive()判斷線程是否執行完成,如果isalive為True表示線程在規定時間內沒有執行完,線程超時。如果join(timeout=None)則會等待線程執行完畢后才會執行join()后面的代碼,一般用于等待線程結束。

name:獲取線程名。

getName():獲取線程名。

setName(name):設置線程名。

ident:“線程標識符”,如果線程尚未啟動,則為None。如果線程啟動是一個非零整數。

is_alive():判斷線程的存活狀態,在run()方法開始之前,直到run()方法終止之后。如果線程存活返回True,否則返回False。

daemon:如果thread.daemon=True表示該線程為守護線程,必須在調用Start()之前設置此項,否則將引發RuntimeError。默認為False

isDaemon():判斷一個線程是否是守護線程。

setDaemon(daemonic):設置線程為守護線程。


?

二、Thread的簡單使用

?1、創建線程:

from threading import Threaddef work(args,kwargs=None):print(args)print(kwargs)if __name__ == "__main__":t = Thread(target=work, args=(("我是位置參數"),), kwargs={'kwargs': '我是關鍵字參數'}, name='我是線程demo')print(t.name) # 打印線程名t.start() # 開啟線程print('我是主線程')# 打印內容如下 我是線程demo 我是位置參數 我是關鍵字參數 我是主線程

由上面的打印內容我們可以看出,在執行完所有線程后才執行的主線程print。如果是多進程的話會先執行主進程中的print然后才會執行子進程的print。主要是因為開啟進程相比于開啟線程更加耗費時間。

2、自定義線程類:

from threading import Threaddef work(args,kwargs=None):print(args)print(kwargs)
class MyThread(Thread): # 使用繼承Thread的方式,自定義線程類def __init__(self,target=None, name=None,args=(), kwargs=None, *, daemon=None):# 如果要給對象封裝屬性,必須先調用父類super().__init__()if kwargs is None:kwargs = {}self._target = targetself._name = nameself._args = argsself._kwargs = kwargsdef run(self): # 必須要有run類,因為start要調用print(f"我重寫了Thread類的run")self._target(*self._args,**self._kwargs)if __name__ == "__main__":t = MyThread(target=work,args=(('我是位置參數'),),kwargs={'kwargs':'我是關鍵字參數'},name='我是自定義線程類') # 創建線程對象print(t.name) # 打印線程名t.start() # 開啟線程print("主線程")# 打印內容如下 我是自定義線程類 我重寫了Thread類的run 主線程 我是位置參數 我是關鍵字參數

3、多進程和多線程的效率對比:

from threading import Thread from multiprocessing import Process import timedef thread_work(name):print(f"{name}") def process_work(name):print(f"{name}")if __name__ == "__main__":# 進程執行效率pro = []start = time.time()for i in range(3):p = Process(target=process_work,args=(("進程-"+str(i)),))p.start()pro.append(p)for i in pro:i.join()end = time.time()print("進程運行了:%s" %(end - start))# 線程執行效率thread_l = []start = time.time()for i in range(3):t = Thread(target=process_work, args=(("線程-" + str(i)),))t.start()thread_l.append(t)for i in thread_l:i.join()end = time.time()print("進程運行了:%s" % (end - start))# 打印內容如下 進程-0 進程-1 進程-2 進程運行了:0.18501067161560059 線程-0 線程-1 線程-2 進程運行了:0.004000186920166016

我們可以從時間上看出,線程的效率是遠遠高于進程的。

4、守護線程

主線程會等待所有非守護線程執行完畢后,才結束主線程。主進程是進程內的代碼結束后就結束主進程。

對比守護進程,代碼執行完畢后立即關閉守護進程,因為在主進程看來代碼執行完畢,主進程結束了,所以守護進程在代碼結束后就被結束了。

守護線程會等待主線程的結束而結束。這是因為如果主線程結束意味著程序結束,主線程會一直等著所有非守護線程結束,回收資源然后退出程序,所以當所有非守護線程結束后,守護線程結束,然后主線程回收資源,結束程序。

下面對比守護進程和守護線程的示例:

4.1 先來看守護進程:

from multiprocessing import Processdef process_work(name):print(f"{name}")if __name__ == "__main__":p = Process(target=process_work,args=("守護進程"))p.daemon=Truep.start()print("主進程")# 打印內容如下 主進程

只打印了主進程,也就是說守護進程還沒來得及執行程序就結束了。

4.2 再來看守護線程:

from threading import Threaddef thread_work(name):print(name)if __name__ == "__main__":t = Thread(target=thread_work,args=("守護線程",))t.daemo=Truet.start()print("\n主線程")# 打印內容如下 守護線程 主線程

也許你會說是由于線程太快了,所以才執行了守護線程。下面我們在線程中阻塞一段時間,在來看看會發生什么效果。

from threading import Thread import time def thread_work(name):time.sleep(3) # 阻塞3秒print(name)if __name__ == "__main__":t = Thread(target=thread_work,args=("守護線程",))t.daemo=Truet.start()print("\n主線程")# 打印內容如下 主線程 守護線程

守護線程還是被執行了,如果是守護進程,守護進程里的代碼是不會被執行的。

?


?

三、線程鎖Lock

Lock也稱線程同步鎖,互斥鎖,原子鎖,該對象只有兩個方法:

acquire(blocking=True, timeout=-1):加鎖。

參數:

blocking:當為True時表示加鎖,只允許一個線程執行被加鎖的代碼。直到遇到release()解鎖后其它線程才可以執行加鎖部分的代碼。當為False時表示不加鎖,并且不能調用release()否則會報RuntimeError。

timeout:設置加鎖時間,單位為秒。-1表示一直等待線程release()后,才允許其它線程執行加鎖的代碼。

release():釋放鎖。

1、未加鎖的代碼示例

from threading import Thread import timedef work():global ntemp = ntime.sleep(0.1) # 由于線程太快了,所以這里停頓下n = temp -1if __name__ == "__main__":n = 100t_l = []for i in range(100):t = Thread(target=work,args=())t.start()t_l.append(t)for i in t_l:i.join()print(n)# 打印內容如下 99

在我們看來其實值應該是0的但卻是99,就因為短暫停了0.1秒導致結果發生了變化。而我們這0.1秒的停留是模擬網絡延遲或者進程調度等原因。造成了數據的結果的錯亂。這個時候我們就需要線程鎖來保證數據的安全性。

2、下面我們就通過給程序加鎖,來保證數據的安全性:

from threading import Thread,Lock import timedef work(lock):lock.acquire() # 加鎖global ntemp = ntime.sleep(0.1) # 由于線程太快了,所以這里停頓下n = temp -1lock.release() # 解鎖if __name__ == "__main__":n = 100t_l = []lock = Lock() # 得到一把鎖對象for i in range(100):t = Thread(target=work,args=(lock,))t.start()t_l.append(t)for i in t_l:i.join()print(n)# 打印內容如下 0

我們會發現程序和上一個示例的運行效率上有著很大的差別。明顯加鎖后程序的運行效率降低了,我們管這種鎖叫做線程同步鎖,使原本并行的程序編程了串行所以程序的效率會慢很多,但是程序的運行結果是正確的。在程序的運行效率和數據的正確性,我們應首先確保程序的正確性然后在考慮程序的效率。

?


?

四、遞歸鎖RLock

死鎖與遞歸鎖:

所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程中,出現了一種互相等待的情況,它們都將無法進行下去。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程,如下模擬產生死鎖。

from threading import Lock,Threaddef work():# 獲取鎖 lock.acquire()print("我是工作線程 1")# 釋放鎖 lock.release()def work_2():# 獲取鎖 lock.acquire()print("我是工作線程 2")work() # 調用工作線程1,造成死鎖# 釋放鎖 lock.release()if __name__ == '__main__':# 生成lock實例lock = Lock()# 開始3個線程for i in range(3):t = Thread(target=work_2)t.start()print('我是主線程')# 打印內容如下 我是主線程 我是工作線程 2 # 此時程序處于死鎖狀態

我們可以理解為一把鎖對象只能創建一把鎖,這把鎖必須release后,才能再次使用。否則程序就會被鎖住,等待解鎖。如上面的代碼,work_2執行創建一個lock鎖,調用work又遇到一把lock鎖,而在work_2中的lock鎖沒有被解鎖,所以程序在work中等待lock解鎖,最終造成了程序出現了死鎖,下面我們使用遞歸鎖RLock來解決上面的問題。

關于RLock的用法及方法一樣,所以這里就不再重復了。

使用RLock避免死鎖:

from threading import RLock,Threaddef work():# 獲取鎖 lock.acquire()print("我是工作線程 1")# 釋放鎖 lock.release()def work_2():# 獲取鎖 lock.acquire()print("我是工作線程 2")work() # 調用工作線程1,造成死鎖# 釋放鎖 lock.release()if __name__ == '__main__':# 使用遞歸鎖RLocklock = RLock()# 開始3個線程for i in range(3):t = Thread(target=work_2)t.start()print('我是主線程')# 打印內容如下 我是工作線程 2 我是工作線程 1 我是工作線程 2 我是工作線程 1 我是工作線程 2 我是主線程 我是工作線程 1

使用遞歸鎖后程序運行正常了。

?


?

五、條件對象Condition(lock=None)

Condition條件變量,與鎖相關聯,在實例化對象時可以給其傳入一把鎖,如果不傳,會默認創建一把遞歸鎖。目前我對它的就理解是它是一把帶通知,掛起線程功能的鎖。它可以掛起線程,然后發送通知激活線程,并且還可以加鎖,屬于一把高級鎖,下面我們來看看常用的方法。

1、Condition類的方法

class threading.Condition([lock])

acquire():加鎖、與Lock、RLock中的用法一致,這里不過多解釋。

release():解鎖、與Lock、RLock中的用法一致,這里不過多解釋

wait(timeout=None):掛起線程,如果timeout是None則必須等到notify或notify_all后線程才會被激活,并且被激活的線程會重新獲取到一把鎖,線程被激活后從wait掛起的位置繼續向下執行。如果指定timeout超時時間,單位為秒,表示線程掛起一段時間后在繼續執行。注意:如果調用線程在調用此方法時未獲取鎖,則會引發RuntimeError。

wait_for(predicate, timeout=None):這個不知道該如何解釋。

notify(n=1):激活被掛起的線程,n表示激活n個被掛起的線程,注意:如果調用線程在調用此方法時未獲取鎖,則會引發RuntimeError。

notify_all():激活所有被掛起的線程,注意:如果調用線程在調用此方法時未獲取鎖,則會引發RuntimeError。

2、下面是一個無聊的實例:

from threading import Condition,Thread import timedef consume():'''消費者'''global cvglobal numcv.acquire()while True:num -= 1if num <= 0:print('-' * 20)cv.notify() # 激活生產者線程 cv.wait()print(f'消費數據-{num}')time.sleep(2)cv.release()def produce():'''生產者'''global cvglobal numcv.acquire()while 1:num += 1print(f'生產數據-{num}')if num >= 5:print('-' * 20)cv.notify() # 激活消費者線程 cv.wait()time.sleep(0.5)cv.release()if __name__ == '__main__':cv = Condition() # 實例化條件對象num = 0# 開啟線程produce_t = Thread(target=produce, args=())consume_t = Thread(target=consume, args=())produce_t.start()consume_t.start()

?

六、Threading類的方法:

threading.active_count():獲取當前活動的線程對象數量。

threading.current_thread():獲取當前的線程對象。如果調用方的控制線程不是通過線程模塊創建的,則返回功能有限的虛擬線程對象。

threading.get_ident():獲取線程標識符。

threading.enumerate():這個比較厲害,可以獲取當前活動的所有線程對象的列表。該列表包括后臺線程和使用current_thread()創建的虛擬線程。以列表的形式返回。

threading.main_thread():獲取主線程對象。

?


?

關于Threading模塊就簡單介紹到這里吧。參考文檔https://docs.python.org/3/library/threading.html#threading.Condition.notify

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

轉載于:https://www.cnblogs.com/caesar-id/p/10764710.html

總結

以上是生活随笔為你收集整理的python线程(二)代码部分Threading模块的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。