python之路-day33-線程相關1

 

1、線程的理論python

  一、什麼是線程多線程

   1) 在傳統操做系統中,每一個進程有一個地址空間,並且默認就有一個控制線程ide

    2)線程顧名思義,就是一條流水線工做的過程(流水線的工作須要電源,電源就至關於cpu),而一條流水線必須屬於一個車間,ui

  一個車間的工做過程就是一個進程,車間負責把資源整合到一塊兒,是一個資源單位,而一個車間至少有一條流水線。 spa

    3)因此:進程只是用來把資源集中到一塊兒(進程只是一個資源單位,或者時候資源集合),而線程纔是cpu上的執行單位操作系統

    4) 多線程(即多個控制線程)的概念是,在一個進程中存在多個線程,多個線程共享該進程的地址空間,至關與一個車間內有線程

  多天流水線,都公用一個車間的資源。例如,北京地鐵與上海地鐵是不一樣的進程,而北京地鐵裏的13號線是一個線程,北京地鐵全部設計

  的線路共享北京地鐵全部的資源,好比全部的乘客能夠被全部線路拉。3d

 

  二、線程與進程的區別code

  1)同一個進程內的多個線程共享該進程內的地址資源

  2)建立線程的開銷要遠小於建立進程的開銷(建立一個進程,就是建立一個車間,設計到申請空間,並且在該空間

  內至少建一條流水線,但建立線程,就只是在一個車間內造一條流水線,無需申請空間,因此建立開銷小)

 

2、開啓線程的兩種方式

  一、threading模塊介紹

  multiprocess模塊的徹底模仿了threading模塊的接口,兩者在使用層面,有很大的類似性,由於不在詳細的介紹

 

  二、開啓線程的兩種方式

 1 # 開啓線程的兩種方式
 2 
 3 # 方式一
 4 # from threading import Thread
 5 # import time
 6 #
 7 # def sayhi(name):
 8 #     time.sleep(2)
 9 #     print('%s say hello'%name)
10 #
11 # if __name__ == '__main__':
12 #     t = Thread(target=sayhi,args=('egon',))
13 #     t.start()
14 #     print('主線程')
15 
16 # 方式二
17 import time
18 from threading import Thread
19 
20 class Sayhi(Thread):
21     def __init__(self,name):
22         super().__init__()
23         self.name = name
24 
25     def run(self):
26         time.sleep(2)
27         print('%s say hello'%self.name)
28 
29 if __name__ == '__main__':
30     t = Sayhi('egon')
31     t.start()
32     print('主線程')
建立線程的兩種方式

 

3、多線程和多進程的區別

  一、誰的開啓速度快

from threading import Thread

def work():
    print('hello')

if __name__ == '__main__':
    t = Thread(target=work)
    t.start()
    print('主線程/主進程')

執行結果以下,幾乎t.start()的同時就將線程開啓了,而後打印出了hello,證實  線程的建立開銷極小

hello
主線程/主進程

  

from multiprocessing import Process

def work():
    print('hello')

if __name__ == '__main__':
    #在主進程下開啓子進程
    p = Process(target=work)
    p.start()
    print('主進程/主線程')

# 執行結果以下,p.start()將開啓進程的信號發給操做系統,操做系統要申請內存
# 空間,拷貝父進程地址空間到子進程,開銷遠大於線程
# 主進程/主線程
# hello

  

  二、瞅一瞅 pid

 1 # 在主進程下開啓多個線程,每一個線程都跟主進程的pid同樣
 2 # from threading import Thread
 3 # import os
 4 #
 5 # def work():
 6 #     print('hello',os.getpid())
 7 #
 8 # if __name__ == '__main__':
 9 #     t1 = Thread(target=work)
10 #     t2 = Thread(target=work)
11 #     t1.start()
12 #     t2.start()
13 #     print('主線程/主進程pid',os.getpid())
14 #
15 # 結果:
16 # hello 9856
17 # hello 9856
18 # 主線程/主進程pid 9856
19 
20 # 開多個進程,每一個進程都有不一樣的pid
21 # from multiprocessing import Process
22 # import os
23 # 
24 # def work():
25 #     print('hello',os.getpid())
26 # 
27 # if __name__ == '__main__':
28 #     p1 = Process(target=work)
29 #     p2 = Process(target=work)
30 #     p1.start()
31 #     p2.start()
32 #     print('主進程/主線程',os.getpid())
33 # 
34 # 結果:
35 # 主進程/主線程 38384
36 # hello 37008
37 # hello 36496
線程、進程中的pid

 

  三、同一進程內的線程共享該進程的數據?

 1 # 進程之間地址空間是隔離的
 2 # from multiprocessing import Process
 3 # import os
 4 #
 5 # def work():
 6 #     global n
 7 #     n = 0
 8 #
 9 # if __name__ == '__main__':
10 #     n = 100
11 #     p = Process(target=work)
12 #     p.start()
13 #     p.join()
14 #     print('主',n)
15 #
16 # 結果分析:毫無疑問子進程p已將本身的全局n改爲了0.可是改的僅僅是本身的
17 # 父進程的n仍然爲100
18 
19 
20 #  同一進程內開啓的多個線程是共享該進程地址空間的
21 # from threading import Thread
22 # import os
23 # 
24 # def work():
25 #     global n
26 #     n = 0
27 # if __name__ == '__main__':
28 #     n = 100
29 #     t = Thread(target=work)
30 #     t.start()
31 #     t.join()
32 #     print('主',n)
33 # 結果:查看結果爲0,由於統一進程內的線程之間共享進程內的數據
34 # 主 0
進程與進程\進程與線程之間數據問題

 

 

4、Thread 對象的其餘屬性或方法

  介紹:

# isAlive():    返回線程是否活動的。
# getName():    返回線程名。
# setName():    設置線程名

threading 模塊提供的一些方法:

# threading.currentThread():    返回當前線程變量
# threading.enumerate():    返回一個包含正在運行的線程的list。正在運行指線程啓動後、結束前,不包括啓動前和終止後的線程
# threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果

  驗證:

 1 # from threading import Thread
 2 # import threading
 3 # from multiprocessing import Process
 4 # import os,time
 5 #
 6 # def work():
 7 #     time.sleep(3)
 8 #     print(threading.current_thread().getName())
 9 # if __name__ == '__main__':
10 #     # 在主進程下開啓線程
11 #     t = Thread(target=work)
12 #     t.start()
13 #     print(threading.current_thread().getName())
14 #     print(threading.current_thread())
15 #     print(threading.enumerate())
16 #     print(threading.activeCount)
17 #     print('主進程\主線程')
18 #
19 # 結果:
20 # MainThread
21 # <_MainThread(MainThread, started 39404)>
22 # [<_MainThread(MainThread, started 39404)>, <Thread(Thread-1, started 39468)>]
23 # <function active_count at 0x000001CBF2F45BF8>
24 # 主進程\主線程
25 # Thread-1
Thread對象的其餘屬性方法
 1 # 主線程等待子進程結束
 2 # from threading import Thread
 3 # import time
 4 # def sayhi(name):
 5 #     time.sleep(2)
 6 #     print('%s say hello'%name)
 7 # if __name__ == '__main__':
 8 #     t = Thread(target=sayhi,args=('egon',))
 9 #     t.start()
10 #     t.join()
11 #     print('主進程')
12 #     print(t.is_alive())
13 # 
14 # 執行結果:
15 # egon say hello
16 # 主進程
17 # False
主線程等待子線程結束

 

 

5、守護線程

  不管是進程仍是線程,都遵循:守護xxx會主動等待主xxx運行完畢後被銷燬

  須要強調的是:運行完畢並不是終止運行

  

一、對主進程來講,運行完畢指的是主進程代碼運行完畢

二、對主線程來講,運行完畢指的是主線程所在的進程內全部非守護線程通通運行完畢,主線程纔算運行完畢


詳細解釋:
一、主進程在其代碼結束後就已經運算完畢了(守護進程在此時就被回收),而後主進程
會一直等飛受賄的子進程都運行完畢後回收子進程的資源(不然會產生殭屍進程),才結束

二、主線程在其餘費守護線程運行完畢後纔算運行完畢(守護線程在刺死就被回收)。由於主線程的結束意味着進程的結束,基礎訥航總體的資源都將被回收,而基礎訥航必須保證非守護線程都運行完畢後才能結束。

  驗證:

# from threading import Thread
# import time
# def sayhi(name):
#     time.sleep(2)
#     print('%s say hello'%name)
# 
# if __name__ == '__main__':
#     t = Thread(target=sayhi,args=('egon'))
#     t.setDaemon(True) # 必須在t.start()以前設置
#     t.start()
#     print('主線程')
#     print(t.is_alive())
# 
# 執行結果:
# 主線程
# True
守護線程驗證

 

 

6、死鎖現象與遞歸鎖

  一、所謂死鎖:是指兩個或兩個以上的進程或線程執行過程當中,由於爭奪資源而形成的一種互相等待的現象,若無外力

  做用,它們都將沒法推動下去。此時稱系統處於死鎖或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程,以下就是死鎖

 1 # from threading import Thread,Lock
 2 # import time
 3 # 
 4 # muteA = Lock()
 5 # muteB = Lock()
 6 # 
 7 # class MyThread(Thread):
 8 #     def run(self):
 9 #         self.fun1()
10 #         self.fun2()
11 # 
12 #     def fun1(self):
13 #         muteA.acquire()
14 #         print('%s拿到A鎖'%self.name)
15 # 
16 #         muteB.acquire()
17 #         print('%s 拿到B鎖'%self.name)
18 #         muteB.release()
19 #         muteA.release()
20 # 
21 #     def fun2(self):
22 #         muteB.acquire()
23 #         print("%s 拿到B鎖"%self.name)
24 #         time.sleep(2)
25 # 
26 #         muteA.acquire()
27 #         print("%s 拿到A鎖"%self.name)
28 #         muteA.release()
29 # 
30 #         muteB.release()
31 # 
32 # if __name__ == '__main__':
33 #     for i in range(10):
34 #         t = MyThread()
35 #         t.start()
36 #         
37 # 結果:
38 # Thread-1拿到A鎖
39 # Thread-1 拿到B鎖
40 # Thread-1 拿到B鎖
41 # Thread-2拿到A鎖
死鎖

  二、遞歸鎖

  解決辦法:遞歸鎖,在python中爲了支持在同一線程中屢次請求同一資源,python提供了可重入鎖RLock個RLock內部維護着

一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源能夠被屢次require。直到一個線程全部的acquire都

被release,其餘的線程才能得到資源。上面的例子若是使用RLock代替Lock,則不會發生死鎖,兩者的區別是:遞歸鎖能夠連續

acquire屢次,而互斥鎖只能acquire一次

 1 from threading import Thread,RLock
 2 import time
 3 
 4 mutexA=mutexB=RLock() #一個線程拿到鎖,counter加1,該線程內又碰到加鎖的狀況,則counter繼續加1,這期間全部其餘線程都只能等待,等待該線程釋放全部鎖,即counter遞減到0爲止
 5 
 6 class MyThread(Thread):
 7     def run(self):
 8         self.func1()
 9         self.func2()
10     def func1(self):
11         mutexA.acquire()
12         print('\033[41m%s 拿到A鎖\033[0m' %self.name)
13 
14         mutexB.acquire()
15         print('\033[42m%s 拿到B鎖\033[0m' %self.name)
16         mutexB.release()
17 
18         mutexA.release()
19 
20     def func2(self):
21         mutexB.acquire()
22         print('\033[43m%s 拿到B鎖\033[0m' %self.name)
23         time.sleep(2)
24 
25         mutexA.acquire()
26         print('\033[44m%s 拿到A鎖\033[0m' %self.name)
27         mutexA.release()
28 
29         mutexB.release()
30 
31 if __name__ == '__main__':
32     for i in range(10):
33         t=MyThread()
34         t.start()
遞歸鎖
相關文章
相關標籤/搜索