Python-多線程之threading


------------恢復內容開始------------

線程基礎

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,將阻塞線程,否則立刻返回

 

 

 

 

------------恢復內容結束------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM