1. 程式人生 > >Python學習筆記__10.2章 多線程

Python學習筆記__10.2章 多線程

編程語言 Python

# 這是學習廖雪峰老師python教程的學習筆記

1、概覽

多任務可以由多進程完成,也可以由一個進程內的多線程完成。進程是由若幹線程組成的,一個進程至少有一個線程。

由於線程是操作系統直接支持的執行單元,因此,高級語言通常都內置多線程的支持,Python也不例外,並且,Python的線程是真正的Posix Thread,而不是模擬出來的線程。

Python的標準庫提供了兩個模塊:_threadthreading_thread是低級模塊。threading是高級模塊,對_thread進行了封裝。絕大多數情況下,我們只需要使用threading這個高級模塊。

1.1、創建一個線程

啟動一個線程就是把一個函數傳入並創建

Thread實例,然後調用start()開始執行:

import time, threading

# 新線程執行的代碼:

def loop():

print('thread %s is running...' % threading.current_thread().name) #打印當前線程實例,此函數體內,都為LoopThread

n = 0

while n < 5:

n = n + 1

print('thread %s >>> %s' % (threading.current_thread().name, n))

time.sleep(1)

print('thread %s ended.' % threading.current_thread().name)

print('thread %s is running...' % threading.current_thread().name) #打印當前線程實例,此處為MainThread

t = threading.Thread(target=loop, name='LoopThread') # 線程執行loop()函數,線程名為‘LoopThread

t.start() #啟動線程

t.join()

#等待線程運行完畢

print('thread %s ended.' % threading.current_thread().name)

threading模塊的current_thread()函數,它永遠返回當前線程的實例。主線程實例的名字叫MainThread,子線程的名字在創建時指定。如果不指定Python就自動給線程命名為Thread-1,Thread-2……

2Lock

多線程和多進程最大的不同在於,多進程中,同一個變量,各自有一份拷貝存在於每個進程中,互不影響,而多線程中,所有變量都由所有線程共享,所以,任何一個變量都可以被任何一個線程修改,因此,線程之間共享數據最大的危險在於多個線程同時改一個變量,把內容給改亂了

2.1 更改全局變量balance

import time, threading

balance = 0

lock = threading.Lock() # threading.Lock(),創建一個鎖

def change_it(n):

# 先加後減,結果應該為0:

global balance

balance = balance + n

balance = balance - n

def run_thread(n):

for i in range(100000):

# 先要獲取鎖:

lock.acquire()

try:

# 進行修改:

change_it(n)

finally:

# 改完了一定要釋放鎖:

lock.release()

t1 = threading.Thread(target=run_thread, args=(5,)) #創建線程,讓t1指向這個線程

t2 = threading.Thread(target=run_thread, args=(8,)) #創建線程,讓t2指向這個線程

t1.start()

t2.start()

t1.join()

t2.join()

print(balance)

當多個線程同時執行lock.acquire()時,只有一個線程能成功地獲取鎖,然後繼續執行代碼,其他線程就繼續等待直到獲得鎖為止

鎖的好處:

  • 是確保了某段關鍵代碼只能由一個線程從頭到尾完整地執行

鎖的壞處:

  • 阻止了多線程並發執行,包含鎖的某段代碼實際上只能以單線程模式執行,效率就大大地下降了。

  • 由於可以存在多個鎖,不同的線程持有不同的鎖,並試圖獲取對方持有的鎖時,可能會造成死鎖,導致多個線程全部掛起,既不能執行,也無法結束,只能靠操作系統強制終止。

3、多核CPU

3.1 死循環測試CPU 使用率

import threading, multiprocessing

def loop(): # 死循環

x = 0

while True:

x = x ^ 1

for i in range(multiprocessing.cpu_count()): # multiprocessing.cpu_count()=cpu 邏輯核數量

t = threading.Thread(target=loop)

t.start()

啟動與CPU核心數量相同的N個線程,在4核CPU上可以監控到CPU占用率僅有102%,也就是僅使用了一核。

用C、C++或Java來改寫相同的死循環,直接可以把全部核心跑滿。但Python不行

因為Python的線程雖然是真正的線程,但解釋器執行代碼時,有一個GIL鎖:Global Interpreter Lock,任何Python線程執行前,必須先獲得GIL鎖,然後,每執行100條字節碼,解釋器就自動釋放GIL鎖,讓別的線程有機會執行。這個GIL全局鎖實際上把所有線程的執行代碼都給上了鎖,所以,多線程在Python中只能交替執行,即使100個線程跑在100核CPU上,也只能用到1個核

Python雖然不能利用多線程實現多核任務,但可以通過多進程實現多核任務。多個Python進程有各自獨立的GIL鎖,互不影響。

4、小結

多線程編程,模型復雜,容易發生沖突,必須用鎖加以隔離,同時,又要小心死鎖的發生。

Python解釋器由於設計時有GIL全局鎖,導致了多線程無法利用多核。多線程的並發在Python中就是一個美麗的夢。


Python學習筆記__10.2章 多線程