第三十三天- 線程創建、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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...