文章目錄
關於什麼是多線程,就不記錄了,我隨便百度都是一堆堆,主要是記錄對自己有用處的筆記,在這裏我主要是使用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()