Python-threading多線程進階(線程守護、線程同步-互斥鎖和遞歸鎖、BoundedSemaphore、Event)

關於什麼是多線程,就不記錄了,我隨便百度都是一堆堆,主要是記錄對自己有用處的筆記,在這裏我主要是使用threading模塊

多線程(複習)

一般創建方式(不常用)

注意:args參數爲一個元組,如果只有一個參數別忘了後面的逗號

import threading
import time

def run(n):
    print("hello", n)
    time.sleep(1)

if __name__ == '__main__':
    t1 = threading.Thread(target=run, args=("threading1",))
    t2 = threading.Thread(target=run, args=("threading2",))
    t1.start()
    t2.start()

自定義線程(繼承threading.Thread)

本質:重構run方法

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, item):
        threading.Thread.__init__(self)
        self.item = item

    def run(self):
        print("threadingTask", self.item)
        time.sleep(1)
       

if __name__ == "__main__":
    t1 = MyThread("threading1")
    t2 = MyThread("threading2")
    t1.start()
    t2.start()

進階知識

守護線程

這裏使用setDaemon(True)把所有的子線程都變成了主線程的守護線程,因此當主進程結束後,子線程也會隨之結束。所以當主線程結束後,整個程序就退出了。

import threading
import time


class MyThread(threading.Thread):
    def __init__(self, item,t):
        threading.Thread.__init__(self)
        self.item = item
        self.t = t

    def run(self):
        print("threadingTask", self.item)
        time.sleep(self.t)
        print(self.item+"結束")


if __name__ == "__main__":
    t1 = MyThread("threading1",1)
    t2 = MyThread("threading2",10)
    t3 = MyThread("threading3",100)
    t2.setDaemon(True)
    t3.setDaemon(True)
    t1.start()
    t2.start()
    t3.start()

運行結果:
在這裏插入圖片描述
設置守護線程之後,當主線程結束時,子線程也將立即結束,不再執行

讓主線程等待子線程結束

當然下面例子中設置線程守護意義不大,只是想強調一點,把子進程設置爲守護線程,必須在start()之前設置

import threading
import time


class MyThread(threading.Thread):
    def __init__(self, item, t):
        threading.Thread.__init__(self)
        self.item = item
        self.t = t

    def run(self):
        print("threadingTask", self.item)
        time.sleep(self.t)
        print(self.item + "結束")


if __name__ == "__main__":
    t1 = MyThread("threading1", 1)
    t2 = MyThread("threading2", 2)
    t3 = MyThread("threading3", 3)
    t2.setDaemon(True)
    t3.setDaemon(True)
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()

線程共享全局變量

線程是進程的執行單元,進程是系統分配資源的最小單位,所以在同一個進程中的多線程是共享資源的。而線程可以共享全局變量感覺沒啥說的,網上抄來一個代碼,複習用
import threading
import time

g_num = 100

def work1():
global g_num
for i in range(3):
g_num += 1
print(“in work1 g_num is : %d” % g_num)

def work2():
global g_num
print(“in work2 g_num is : %d” % g_num)

if name == ‘main’:
t1 = threading.Thread(target=work1)
t1.start()
time.sleep(1)
t2 = threading.Thread(target=work2)
t2.start()

線程同步

以下內容部分來自菜鳥教程
如果多個線程共同對某個數據修改,則可能出現不可預料的結果,爲了保證數據的正確性,需要對多個線程進行同步。多線程的優勢在於可以同時運行多個任務(至少感覺起來是這樣)。但是當線程需要共享數據時,可能存在數據不同步的問題。

考慮這樣一種情況:一個列表裏所有元素都是0,線程"set"從後向前把所有元素改成1,而線程"print"負責從前往後讀取列表並打印。

那麼,可能線程"set"開始改的時候,線程"print"便來打印列表了,輸出就成了一半0一半1,這就是數據的不同步。爲了避免這種情況,引入了鎖的概念。

鎖有兩種狀態——鎖定和未鎖定。每當一個線程比如"set"要訪問共享數據時,必須先獲得鎖定;如果已經有別的線程比如"print"獲得鎖定了,那麼就讓線程"set"暫停,也就是同步阻塞;等到線程"print"訪問完畢,釋放鎖以後,再讓線程"set"繼續。

經過這樣的處理,打印列表時要麼全部輸出0,要麼全部輸出1,不會再出現一半0一半1的尷尬場面。

互斥鎖

#!/usr/bin/python3

import threading
import time


class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter

    def run(self):
        print("開啓線程: " + self.name)
        # 獲取鎖,用於線程同步
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # 釋放鎖,開啓下一個線程
        threadLock.release()


def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1


threadLock = threading.Lock()
threads = []

# 創建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開啓新線程
thread1.start()
thread2.start()

# 添加線程到線程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有線程完成
for t in threads:
    t.join()
print("退出主線程")

遞歸鎖

RLcok類的用法和Lock類一模一樣,但它支持嵌套,在多個鎖沒有釋放的時候一般會使用RLcok類。

#!/usr/bin/python3

import threading
import time


class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter

    def run(self):
        print("開啓線程: " + self.name)
        # 獲取鎖,用於線程同步
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # 釋放鎖,開啓下一個線程
        threadLock.release()


def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1


threadLock = threading.RLock()
threads = []

# 創建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開啓新線程
thread1.start()
thread2.start()

# 添加線程到線程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有線程完成
for t in threads:
    t.join()
print("退出主線程")

BoundedSemaphore類

互斥鎖同時只允許一個線程更改數據,而Semaphore是同時允許一定數量的線程更改數據

#!/usr/bin/python3

import threading
import time


class myThread(threading.Thread):
    def __init__(self, threadID, name, counter, semaphore):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
        self.semaphore = semaphore

    def run(self):
        print("開啓線程: " + self.name)
        semaphore.acquire()
        print_time(self.name, self.counter, 3)
        semaphore.release()


def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1


semaphore = threading.BoundedSemaphore(2)
threads = []

for i in range(3):
    t = myThread(i + 1, f"Thread-{i + 1}", i + 1,semaphore)
    threads.append(t)

for i in threads:
    i.start()


while threading.active_count() != 1:
    pass
else:
    print('-----all threads done-----')

事件(Event類)

本部分內容來自[博客],謝謝博主的博客(https://www.cnblogs.com/luyuze95/p/11289143.html#threading%E6%A8%A1%E5%9D%97)
python線程的事件用於主線程控制其他線程的執行,事件是一個簡單的線程同步對象,其主要提供以下幾個方法:

clear 將flag設置爲“False”
set 將flag設置爲“True”
is_set 判斷是否設置了flag
wait 會一直監聽flag,如果沒有檢測到flag就一直處於阻塞狀態
事件處理的機制:全局定義了一個“Flag”,當flag值爲“False”,那麼event.wait()就會阻塞,當flag值爲“True”,那麼event.wait()便不再阻塞。

#利用Event類模擬紅綠燈
import threading
import time

event = threading.Event()


def lighter():
    count = 0
    event.set()     #初始值爲綠燈
    while True:
        if 5 < count <=10 :
            event.clear()  # 紅燈,清除標誌位
            print("\33[41;1mred light is on...\033[0m")
        elif count > 10:
            event.set()  # 綠燈,設置標誌位
            count = 0
        else:
            print("\33[42;1mgreen light is on...\033[0m")

        time.sleep(1)
        count += 1

def car(name):
    while True:
        if event.is_set():      #判斷是否設置了標誌位
            print("[%s] running..."%name)
            time.sleep(1)
        else:
            print("[%s] sees red light,waiting..."%name)
            event.wait()
            print("[%s] green light is on,start going..."%name)

light = threading.Thread(target=lighter,)
light.start()

car = threading.Thread(target=car,args=("MINI",))
car.start()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章