第三十三天- 線程創建、join、守護線程、死鎖

来源:https://www.cnblogs.com/xi1419/archive/2018/12/03/10061053.html
-Advertisement-
Play Games

1.線程,線程創建 概念:在傳統操作系統中,每個進程有一個地址空間,而且預設就有一個控制線程,線程顧名思義,就是一條流水線工作的過程,一條流水線必須屬於一個車間,一個車間的工作過程是一個進程,車間負責把資源整合到一起,是一個資源單位,而一個車間內至少有一個流水線。流水線的工作需要電源,電源就相當於c ...


 

1.線程,線程創建

  概念:在傳統操作系統中,每個進程有一個地址空間,而且預設就有一個控制線程,線程顧名思義,就是一條流水線工作的過程,一條流水線必須屬於一個車間,一個車間的工作過程是一個進程,車間負責把資源整合到一起,是一個資源單位,而一個車間內至少有一個流水線。流水線的工作需要電源,電源就相當於cpu。

  所以,進程只是用來把資源集中到一起(進程只是一個資源單位,或者說資源集合),而線程才是cpu上的執行單位

  多線程(即多個控制線程)的概念是,在一個進程中存在多個控制線程,多個控制線程共用該進程的地址空間,相當於一個車間內有多條流水線,都共用一個車間的資源。

 

  創建:

  線程創建方式一:

 1 from multiprocessing import Process
 2 from threading import Thread
 3 
 4 
 5 def func(n):
 6     print('xxxxx')
 7     print('>>>',n)
 8 
 9 
10 if __name__ == '__main__':
11 
12     # p = Process(target=func,args=(1,))
13     # p.start()
14 
15     t = Thread(target=func,args=(1,))  # 直接創建
16     t.start()
17 
18     print('主線程結束')
View Code

  面向對象創建:

 1 from threading import Thread
 2 
 3 
 4 # 面向對象創建
 5 class Mythread(Thread):  # 繼承Thread父類
 6 
 7     def __init__(self,xx):
 8         super().__init__()
 9         self.xx = xx
10 
11     def run(self):  # 必須有run,覆蓋父類run中的pass
12         print(self.xx)
13         print('我重置父類方法')
14 
15     def func1(self): # 寫其他方法
16         print('我是func1')
17 
18 
19 if __name__ == '__main__':
20 
21     t1 = Mythread('xx')
22     t1.start()  # 預設執行run
23     t1.func1()  # 調用其他方法
24 #
25 
26 from multiprocessing import Process
27 from threading import Thread
View Code

 

 

2.Thread類方法

  join方法:

  主線程等待join子線程執行完畢後才執行

 1 import time
 2 from threading import Thread
 3 
 4 
 5 def func(n):
 6     time.sleep(n)
 7     print('我是子線程')
 8 
 9 
10 if __name__ == '__main__':
11 
12     t = Thread(target=func,args=(1,))
13     t.start()
14 
15     t.join() # 等待子線程執行結束
16     print('我是主線程,子線程結束再執行我')
join

  

  其他方法:

 1 ''
 2 Thread實例對象的方法
 3   # isAlive(): 返回線程是否活動的。
 4   # getName(): 返回線程名。
 5   # setName(): 設置線程名。
 6 
 7 threading模塊提供的一些方法:
 8   # threading.currentThread(): 返回當前的線程變數。
 9   # threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程。
10   # threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果
11 '''
 1 import time, threading
 2 from threading import Thread, current_thread
 3 
 4 
 5 def func():
 6     time.sleep(2)
 7     print('子線程,名字是:', current_thread().getName())  # 返回線程名
 8     print('子線程,ID是:', current_thread().ident)  # 返回線程id
 9 
10 
11 if __name__ == '__main__':
12 
13     for i in range(10):
14         t = Thread(target=func, )
15         t.start()
16 
17     print(threading.enumerate())  # 返回一個包含正在運行的list
18     print(threading.activeCount())  # 返回正在運行的線程數量,等同len(threading.enumerate())
19 
20     print('主線程結束')
其他方法示例

 

 

3.守護線程、事件

  守護線程:

  主進程結束,守護進程跟著結束,再執行非守護進程
  主線程要等待所有非守護線程運行結束才會結束(因為他們屬於同一進程)
  需註意:運行結束並非終止運行

  xxx.setDaemon(True)  或者 xxx.daemon = True

 1 import time
 2 from threading import Thread
 3 from multiprocessing import Process
 4 
 5 
 6 def func1():
 7     time.sleep(3)
 8     print('任務1結束')
 9 
10 
11 def func2():
12     time.sleep(2)
13     print('任務2結束')
14 
15 
16 if __name__ == '__main__':
17 
18     # p1 = Process(target=func1,)
19     # p2 = Process(target=func2,)
20     # # p1.daemon = True
21     # p2.daemon = True
22     # p1.start()
23     # p2.start()
24     #
25     # print('主進程結束')
26 
27     t1 = Thread(target=func1,)
28     t2 = Thread(target=func2,)
29     # t1.setDaemon(True) # 只列印出 主線程和t2 因為t2時間比t1小
30     t2.setDaemon(True)  # 會正常列印出所有 因為t1時間大於t2
31     t1.start()
32     t2.start()
33 
34     print('主線程結束')
守護線程與守護進程

  

  事件: 

  event.isSet():返回event的狀態值;
  event.wait():如果 event.isSet()==False將阻塞線程;
  event.set(): 設置event的狀態值為True,所有阻塞池的線程激活進入就緒狀態, 等待操作系統調度;
  event.clear():恢復event的狀態值為False。

 1 import time
 2 from threading import Event
 3 
 4 e = Event()  # 預設false狀態
 5 print(e.isSet())  # 事件當前狀態
 6 
 7 e.set()  # 改變成Ture
 8 print(e.isSet())
 9 
10 print('稍等...')
11 # e.clear()  # 將e的狀態改為False
12 e.wait()  # 如果 event.isSet()==False將阻塞線程
13 
14 if e.isSet() == True:
15     time.sleep(1)
16     print('滴滴滴,上車吧...')
事件代碼示例

 

 

4.線程間數據問題

  開啟一個線程所需要的時間要遠小於開啟一個進程

 1 import time
 2 from multiprocessing import Process
 3 from threading import Thread
 4 
 5 
 6 def func(i):
 7     return '我是任務%s'%i
 8 
 9 
10 if __name__ == '__main__':
11 
12     # 多線程
13     t_list = []
14     t_start_time = time.time()
15     for i in range(10):
16         t = Thread(target=func,args=(i,))
17         t_list.append(t)
18         t.start()
19 
20     [tt.join() for tt in t_list]
21     t_end_time = time.time()
22     t_dif_time = t_end_time - t_start_time
23 
24     # 多進程
25     p_list = []
26     p_start_time = time.time()
27     for i in range(10):
28         p = Process(target=func,args=(i,))
29         p_list.append(p)
30         p.start()
31 
32     [pp.join() for pp in p_list]
33     p_end_time = time.time()
34     p_dif_time = p_end_time - p_start_time
35     # 結果受cpu影響
36     
37     print('多線程耗時:',t_dif_time)
38     print('多進程耗時:',p_dif_time)
39     print('主線程結束')
40 
41 '''
42 多線程耗時: 0.0020008087158203125
43 多進程耗時: 0.4188823699951172
44 '''
多線程和多進程效率對比

  線程之間共用進程資源(全局變數在多個線程之間共用),但也會導致數據不但全問題

 1 import time
 2 from threading import Thread
 3 
 4 num = 100
 5 
 6 def func():
 7     global num
 8     tep = num  # tep替換 模擬多步操作
 9     time.sleep(0.001)  # 模擬延遲
10     tep -= 1
11     num = tep
12 
13 
14 if __name__ == '__main__':
15 
16     t_list = []
17     for i in range(100):
18         t = Thread(target=func,)
19         t_list.append(t)
20         t.start()
21 
22     [tt.join() for tt in t_list]
23 
24     print('主線程的num',num)  # 列印出不是100可知數據是共用的
25 
26 # 理論上應該是0,但多線程是併發執行的,會出現上一個線程還在運算中時,下一個線程並未等待它返回值
27 # 也拿了原來的值進來運算,所以列印出了91,92,93不等,可知多線程數據是不安全的
28 '''
29 主線程的num 92
30 '''
驗證多線程之間數據共用 數據不安全問題

  加鎖解決多線程數據不安全問題

 1 import time
 2 from threading import Thread,Lock
 3 
 4 num = 100
 5 def func(lock,i):
 6     global num
 7     lock.acquire()  # 加鎖
 8     
 9     tep = num
10     time.sleep(0.001)  # 模擬延遲
11     tep -= 1
12     num = tep
13 
14     lock.release()  # 釋放
15 
16 
17 if __name__ == '__main__':
18     t_list = []
19     lock = Lock()
20     for i in range(100):
21         t = Thread(target=func,args=(lock,i))
22         t_list.append(t)
23         t.start()
24 
25     [tt.join() for tt in t_list]
26     print('主線程num',num)
27 
28 '''
29 主線程num 0
30 '''
Lock

 

  信號量Semaphore

 1 import time,random
 2 from threading import Thread,Semaphore
 3 
 4 
 5 def func(i,s):
 6     s.acquire()
 7     print('%s張燒餅'%i)
 8     time.sleep(random.randint(1,3))
 9     s.release()
10     # 出來一個進去一個 始終6個  最後不足6個就都進來了
11 
12 
13 if __name__ == '__main__':
14     s = Semaphore(6)  # 與Lock類似,不過可限制最大連接數,如這裡同時只有6個線程可以獲得semaphore
15     for i in range(28):
16         t = Thread(target=func,args=(i,s,))
17         t.start()
Semaphore

 

 

5.死鎖和遞歸鎖

  死鎖現象:有多個鎖時,雙方互相等待對方釋放對方手裡拿到的那個鎖導致死鎖   

 1 import time
 2 from threading import Thread,Lock
 3 
 4 
 5 def func1(lock_A,lock_B):
 6     lock_A.acquire()
 7     print('張全蛋拿到了A鎖')
 8     time.sleep(0.5)
 9     lock_B.acquire()
10     print('張全蛋拿到了B鎖')
11     lock_B.release()
12     lock_A.release()
13 
14 
15 def func2(lock_A,lock_B):
16     lock_B.acquire()
17     print('趙二狗拿到了B鎖')
18     lock_A.acquire()
19     print('趙二狗拿到了A鎖')
20     lock_A.release()
21     lock_B.release()
22 
23 
24 if __name__ == '__main__':
25 
26     lock_A = Lock()
27     lock_B = Lock()
28     t1 = Thread(target=func1,args=(lock_A,lock_B,))
29     t2 = Thread(target=func2,args=(lock_A,lock_B,))
30     t1.start()
31     t2.start()
死鎖現象

 

  遞歸鎖:RLock 

  RLock管理一個內置的計數器,
  每當調用acquire()時內置計數器-1;
  調用release() 時內置計數器+1;
  計數器不能小於0;當計數器為0時,acquire()將阻塞線程直到其他線程調用release()。

 

 

 1 # 遞歸鎖解決死鎖現象
 2 import time
 3 from threading import Thread,Lock,RLock
 4 
 5 
 6 def func1(lock_A,lock_B):
 7     lock_A.acquire()
 8     print('張全蛋拿到了A鎖')
 9     time.sleep(0.5)
10     lock_B.acquire()
11     print('張全蛋拿到了B鎖')
12     lock_B.release()
13     lock_A.release()
14 
15 
16 def func2(lock_A,lock_B):
17     lock_B.acquire()
18     print('趙二狗拿到了B鎖')
19     lock_A.acquire()
20     print('趙二狗拿到了A鎖')
21     lock_A.release()
22     lock_B.release()
23 
24 
25 if __name__ == '__main__':
26 
27     # lock_A = Lock()
28     # lock_B = Lock()
29     lock_A = lock_B = RLock()
30     t1 = Thread(target=func1,args=(lock_A,lock_B,))
31     t2 = Thread(target=func2,args=(lock_A,lock_B,))
32     t1.start()
33     t2.start()
遞歸鎖解決死鎖現象

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 學習Java一年多了,練習了很多,這條路真的很難走.還有半年多畢業的我整理整理所學習的筆記給大家分享主要也是讓自己記憶加深.自學時用到的時阿發老師的教學視頻,通俗易懂還有題庫可以練習.最經典的就是阿發老師會教你如何開發車而不是使用車.筆記中的截圖和源碼都是老師的.這次整理主要是給自己看的,有需要的朋 ...
  • 《工作細胞》最近比較火,bilibili 上目前的短評已經有17000多條。 先看分析下頁面 右邊 li 標簽中的就是短評信息,一共20條。一般我們載入大量數據的時候,都會做分頁,但是這個頁面沒有,只有一個滾動條。 隨著滾動條往下拉,信息自動載入了,如下圖,變40條了。由此可見,短評是通過非同步載入的 ...
  • 因為工作中慢慢開始用python的協程,所以想更好的理解一下實現方式,故翻譯此文 原文中把辭彙表放到最後,但是我個人覺得放在最開始比較好,這樣可以增加當你看原文時的理解程度 辭彙表 原生協程函數 Native coroutine function: 由async def定義的協程函數,可以使用awa ...
  • 只使用Spring的時候,我把applicationContext.xml是放在項目的src路徑下的,這樣使用ClassPathXmlApplicationContext很方便嘛 整合了struts之後,就讀取不到這個配置文件了,因為Spring會到WEB-INF下來找配置文件, Spring配置文 ...
  • 1.線程列隊 queue隊列 :使用import queue,用法與進程Queue一樣 class queue.Queue(maxsize=0) 1 # 先進先出: 2 q = queue.Queue(3) # 也可以不加數字表示不限 3 q.put('約嗎') 4 q.put('你個糟老頭') 5 ...
  • 題意 "題目鏈接" 求滿足$i^2 + j^2 \% M = 0$的數對$(i, j)$的個數,$1 \leqslant i, j \leqslant 10^9, M \leqslant 1000$ Sol 發這篇博客的目的就是為了證明一下我到底有多菜。 mdzz小學組水題我想了40min都沒想出來 ...
  • 首先我們來講講我們python中的可變對象和不可變對象: 可變對象:該對象指向記憶體中的值是可以改變的。實際上是其所指的值直接發生改變,而不是發生複製,或者開闢一個新的地址空間。例如:列表list,字典dict,集合set。 不可變對象:該對象所指向的記憶體中的值是不能被改變的。當改變一個變數時,由於其 ...
  • 一、多線程 1、1 線程與進程區別 進程:每個正在系統上運行的程式都是一個進程。每個進程包含一到多個線程。 線程:線程是一組指令的集合,或者是程式的特殊段,它可以在程式里獨立執行。 總結:進程是所有線程的集合,每一個線程是進程中的一條執行路徑。多線程的好處是可以提高程式的效率。 一個應用系統可以多個 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...