------------恢復內容開始------------
線程基礎
1.1線程狀態
線程有五種狀態:新建、就緒、運行、阻塞、死亡
1.2線程同步
因為線程同時運行多個任務,但實際上還是cpu以極快的速度在每個線程之間轉換處理任務,對於python這種高級語言,每條語句的執行都不是簡單的一步到位,因此,在語句執行過程中,還未結束(比如打印兩句話),cpu就運行其他的線程,這可能會導致公共資源發生不好的變化(第一句話還沒完,中間就插入了第二句話),也就是數據不同步了,因此引用了鎖的概念,避免重要數據在處理時被打斷或者其他線程篡改。
鎖有兩種狀態——鎖定、未鎖定,當鎖定的時候,其他的線程執行到鎖定的部分,需要請求鎖,但是鎖一次只供給一條線程,等這條先線程執行到釋放鎖的時候,其他的等待(被阻塞)的線程中,才有一條線程可以獲得鎖(線程進行競爭來獲取鎖,不知道那線程得到鎖),然后繼續執行、釋放等,這樣的話重要數據的操做會完整執行,保證了數據的同步。
線程和鎖的關系
1.3線程通信(條件變量)
線程同步使用了鎖,保證了只讓一條線程執行,其他等待,我們認為其他的線程被阻塞了,且這些線程的等待是被動的,釋放鎖后會立刻競爭鎖。雖然能保證線程不干擾了,但是有一個問題,我們怎么能保證最開始執行的線程時我們向要執行的線程呢,畢竟,線程執行的任務不一定全部一樣,有的線程需要其他線程創造一些條件,才能正常無錯誤的執行,比如a線程創建了一個列表,b線程才可以遍歷列表,如果b線程先遍歷,那么就會報錯。因此引入了條件變量的概念
條件變量,允許在不滿足條件的時候,線程進入等待,當其他線程創造條件后,發出通知,收到通知的線程直到可以干活了,就從掛起狀態運行,這時候,如不收不到通過通知,就會一直掛着。
當一個線程得到鎖定后,判斷是否滿足條件。如果不滿足,則回到等待池(此時自動解鎖),等待條件滿足的通知,然后收到通知后,請求鎖定,直到獲得鎖定,條件滿足,繼續執行,然后釋放鎖給其它線程
掛起相對於阻塞來說,可以看為是一個主動的行為,因為如果一致不滿足,及時鎖釋放了,被掛起的線程也不會取競爭鎖
當滿足某種條件后,認為需要喚醒等待線程來執行任務,就可以使用 nodify() 后者nodify_all()喚起一條或所等待池中的線程
1.4、線程阻塞之間的轉換
阻塞有三種情況:
同步阻塞是指處於競爭鎖定的狀態,線程請求鎖定時將進入這個狀態,一旦成功獲得鎖定又恢復到運行狀態;
等待阻塞是指等待其他線程通知的狀態,線程獲得條件鎖定后,調用“等待”將進入這個狀態,一旦其他線程發出通知,線程將進入同步阻塞狀態,再次競爭條件鎖定;
而其他阻塞是指調用time.sleep()、anotherthread.join()或等待IO時的阻塞,這個狀態下線程不會釋放已獲得的鎖定。
tips: 如果能理解這些內容,接下來的主題將是非常輕松的;並且,這些內容在大部分流行的編程語言里都是一樣的。(意思就是非看懂不可 >_< 嫌作者水平低找別人的教程也要看懂)
python多線程——threading使用
概述:
threading用於提供跟線程相關的操作,線程是應用程序中工作的最小單元。
threading模塊提供的類
Thread、Lock、Rlock、Condition、Semaphore、Event、Timer、local
threading模塊提供的 常用方法
threading.currentThreading() 返回現在進行的線程變量
threading.enumerate() 返回當前正在運行的所有進程的列表
threading.activeCount() 返回活躍的線程的數量,與len(threading.enumrate())相同
Thread類
Thread類是線程類,使用方式為傳入要運行的方法和繼承Thread
import threading import time # method1 將要執行的方法,作為一個參數,在實例化Threading類的時候,作為目標直接傳入 def loop(x,y): print('%s is running' % threading.currentThread()) time.sleep(3) print('%s is end' % threading.currentThread()) return x+y for i in range(4): t = threading.Thread(target=loop,name='線程'+str(i),args=(i,5)) t.start() time.sleep(1) print() print('\nmian thread end') # method2 從Thread類繼承,繼承的對象重寫的調用run方法 class LoopClass(threading.Thread): def __init__(self,name): super(LoopClass,self).__init__() # 顯式調用父類初始化函數(暫時不理解為什么) self.name = name def run(self) -> None: print(self.name,'is runnig' ) print('當前進程有======>',threading.enumerate()) for i in range(4,7): l=LoopClass(i) l.run() # # <Thread(線程0, started 10984)> is running # # <Thread(線程1, started 12096)> is running # # <Thread(線程2, started 956)> is running # <Thread(線程0, started 10984)> is end # # <Thread(線程3, started 4256)> is running # <Thread(線程1, started 12096)> is end # # # mian thread end # 4 is runnig # 當前進程有======> [<_MainThread(MainThread, started 12068)>, <Thread(線程2, started 956)>, <Thread(線程3, started 4256)>] # 5 is runnig # 當前進程有======> [<_MainThread(MainThread, started 12068)>, <Thread(線程2, started 956)>, <Thread(線程3, started 4256)>] # 6 is runnig # 當前進程有======> [<_MainThread(MainThread, started 12068)>, <Thread(線程2, started 956)>, <Thread(線程3, started 4256)>] # <Thread(線程2, started 956)> is end # <Thread(線程3, started 4256)> is end
Thread類構造方法
Thread(group=None,target=None,name=None,args=(),kwarg={})
group:線程組,當前未實現,默認為None
target:可執行的方法
name:線程名
實例方法:
isAlive() 線程是否活躍
setName() 設置線程名
getName() 獲取線程名
start() 啟動線程
is/setDeamon(bool) 獲取/設置為后台線程(默認前台線程為False),在start之前設置
后台線程(守護線程):相當於線程的一種屬性,當主線程執行完的時候,如果后台線程沒有執行結束,也會結束程序
前台線程:當主線程執行結束的時候,如果有前台線程還在執行,則等待前台線程執行結束,然后程序結束
def loop(n): while n: print('%s is running' % threading.currentThread()) time.sleep(2) return 1 for i in range(4): t = threading.Thread(target=loop,name='線程'+str(i),args=(1,)) t.setDaemon(True) # 設置為守護進程, t.start() for i in range(4): td = threading.Thread(target=loop,name='線程'+str(i),args=(2,)) td.setDaemon(True) td.start() print('\nmian thread end') # 當兩個進程都無限循環,但是都被設置為守護進程,主線程不會無限等待,正常終止 # <Thread(線程0, started daemon 4132)> is running # <Thread(線程1, started daemon 11216)> is running # <Thread(線程2, started daemon 11640)> is running # <Thread(線程3, started daemon 5844)> is running # mian thread end # 當某一個進程設置為前台進程,即便主線程執行完,程序也不會結束
join([timeout]) 阻塞上下文環境的線程,直到調用此方法的線程終止,或者達到timeout,(及時線程設置守護線程,也需要等待)
import threading import time def loop1(): # time.sleep(1) print('This is %s is running' % threading.currentThread().getName()) time.sleep(1) print('%s ending' % threading.currentThread().getName()) thread_list = [] for i in range(4): a = threading.Thread(target=loop1) thread_list.append(a) a.start() for thread in thread_list: thread.join() print('this is main ') # This is Thread-1 is running # This is Thread-2 is running # This is Thread-3 is running # This is Thread-4 is running # Thread-1 endingThread-2 endingThread-3 ending # # # Thread-4 ending # this is main # # 進程已結束,退出代碼為 0
Lock和RLock類
線程之間隨機調度,某條線程執行的語句還未結束,cpu就轉換到其他的線程上了,為了避免操作同一個線程的時候,資源不產生混亂,使用鎖,保證公共的變量不被多條線程同時執行。
Lock(指令鎖),是最低級的同步指令,當Lock處在鎖定狀態的時候,不被特定的線程所有,.Lock包含了鎖定、非鎖定兩種狀態,以及aquest和release兩種方法。請求鎖定時,線程將處於鎖定狀態,直到釋放
RLock(可重入鎖)可以被一個線程對此請求的同步指令。RLock有”擁有的線程“和”遞歸等級“的概念,RLock被某個線程所有,該線程可以再次調用acquire(),release()時需要相同的次數
可以認為RLock包含一個計數器,當成功調用acquire/release時,計數器+1/-1,當計數器為0時,認為是未鎖定狀態
相對於Lock,RLock可以重復aquire而不會出現死鎖的情況
lock.require([,timeout])
lock.release()
import threading lock = threading.Lock() # Lock對象 lock.acquire() print(1) lock.acquire() # 產生了死鎖。 print(2) lock.release() print(3) lock.release() print(lock.acquire()) # 無法結束 # 1
import threading
lock = threading.RLock() # RLock對象
lock.acquire()
print(1)
lock.acquire()
print(2)
lock.release()
print(3)
lock.release()
print(lock.acquire())
# 正常結束
# 1
# 2
# 3
# True
Condition
condition(條件變量),是一個與鎖關聯的
可以認為,condition是更強大的帶鎖功能的類,與condition相關的概念有線程的阻塞和掛起
阻塞:因為資源的爭搶,線程被動的停止。當一個線程獲得了鎖,其他的線程都需要等待這個線程釋放鎖之后才能進行,那么這些等待的線程就是被鎖定了
掛起:主動的行為,可以主動掛起某個線程,當這個線程暫時不運行,也能主動放下線程讓其繼續運行
掛起的層級是高於阻塞的:當一個線程阻塞的時候,是可以被掛起的,掛起結束后,如果線程還在阻塞就不會運行。當一個線程掛起的時候,如果這個線程,在掛起期間阻塞消除了,如果不放下的話,線程仍舊無法運行
構造方法Condition([,Lock/RLock])
實例方法:
acquire([,timeout])/release: 請求鎖、釋放鎖
wait([,timeout]):獲取鎖定之后,調用此方法,鎖定的線程將自動解鎖,並處於掛起狀態,收到廣播之后被喚醒,或者timeout超時,才能被喚醒,喚醒后的線程會重新獲得鎖)
notify():通知,調用該方法之后,會隨機喚醒一個被掛起的線程,只是喚起,而不涉及釋放鎖,使用前先成功要已經獲得鎖
notify_all:喚起所有掛起的線程,使用前線程要已經獲得鎖
Event
線程通信機制,一個線程通知事件,其他線程等待事件,Event內置一個False的標志,當調用set()的時候,設置為True,調用Clear的是時候,設置為False,wait([,timeout]) 將根據Event是False還是True,將線程阻塞
實例方法
isSet() : 當內置事件為True時返回True
set():將event內置事件設置為True
clear():將event內置事件設置為False
wait():如果event為False,將阻塞線程,否則立刻返回
------------恢復內容結束------------