Python多線程

1.同時執行多個任務

  •     多進程模式
  •     多線程模式
  •     多進程+多線程

   注:進程是操作系統分配資源的最小單元。線程是操作系統調度的最小單元。

2.多線程編程

  • 多任務可以由多進程完成,也可以由一個進程中的多線程完成(一個進程至少有一個進程)

3.Python中模塊

  • _thread和threading。_thread是低級模塊,threading是高級模塊,對_thread進行了封裝。絕大多數情況下我們使用的是threading高級模塊

3.啓動一個線程就是把一個函數傳入並創建Thread實例,然後調用start()開始執行

import threading
import time


def loop(x):
    print("%s start" % threading.current_thread().name)
    for i in range(x):
        time.sleep(1)
        print("%s : %d" % (threading.current_thread().name, i))
        # print("%s stop" % threading.current_thread().name)
    print("%s stop" % threading.current_thread().name)


print("%s start" % threading.current_thread().name)
t1 = threading.Thread(target=loop, args=(6,))
t1.start()
print("%s stop" % threading.current_thread().name)

結果:

MainThread start

Thread-1 start
MainThread stop
Thread-1 : 0
Thread-1 : 1
Thread-1 : 2
Thread-1 : 3
Thread-1 : 4
Thread-1 : 5

Thread-1 stop

結果分析:主線程先運行完畢,再運行子線程

4.如果希望主線程等待子線程執行完畢,使用join()方法

import threading
import time


def loop(x):
    print("%s start" % threading.current_thread().name)
    for i in range(x):
        time.sleep(1)
        print("%s : %d" % (threading.current_thread().name, i))
        # print("%s stop" % threading.current_thread().name)
    print("%s stop" % threading.current_thread().name)


print("%s start" % threading.current_thread().name)
t1 = threading.Thread(target=loop, args=(6,))
t1.start()
t1.join()
print("%s stop" % threading.current_thread().name)

結果:

MainThread start
Thread-1 start
Thread-1 : 0
Thread-1 : 1
Thread-1 : 2
Thread-1 : 3
Thread-1 : 4
Thread-1 : 5
Thread-1 stop

MainThread stop

結果分析:MainThread 一直停在join位置,直到子線程運行完畢

6.主線程退出的時候,不管子線程運行到哪裏,強制退出子線程,使用setDaemon(True), 這個方法在線程啓動之前調用

import threading
import time


def loop(x):
    print("%s start" % threading.current_thread().name)
    for i in range(x):
        time.sleep(1)
        print("%s : %d" % (threading.current_thread().name, i))
        # print("%s stop" % threading.current_thread().name)
    print("%s stop" % threading.current_thread().name)


print("%s start" % threading.current_thread().name)
t1 = threading.Thread(target=loop, args=(6,))
t1.setDaemon(True)
t1.start()
# t1.join()
print("%s stop" % threading.current_thread().name)

結果:

MainThread start
Thread-1 start

MainThread stop

7.線程鎖(lock)

多線程和多進程最大的不同在於,多進程同一變量各自有一份拷貝存在每個進程中,互不影響,而多線程所有變量由線程共享,所以任何一個變量都可以由任何一個線程修改,因此線程間共享數據最大的危險在與多個線程同時改變一個變量,把內容給改亂了。解決方法:使用threading.Lock()方法,使用線程鎖鎖定線程需要使用的對象,確保一次只有一個線程能夠使用該對象。

實例:多線程操作同一對象出錯

import threading

deposit = 0  # 存款


def change(n):
    global deposit
    deposit = deposit + n
    deposit = deposit - n


def loop(n):
    for i in range(10000000):
        change(n)


t1 = threading.Thread(target=loop, args=(5,))
t2 = threading.Thread(target=loop, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(deposit)

結果:

45

結果分析:t1,t2交替運行,當兩個線程同時訪問一個對象時候,容易出錯


使用線程鎖結果該問題:

import threading

deposit = 0  # 存款


def change(n):
    global deposit
    deposit = deposit + n
    deposit = deposit - n


def loop(n):
    for i in range(10000000):
        lock.acquire()  # 取得一個鎖
        try:
            change(n)
        finally:
            lock.release()  # 釋放一個鎖


lock = threading.Lock()  # 創建一個鎖對象
t1 = threading.Thread(target=loop, args=(5,))
t2 = threading.Thread(target=loop, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(deposit)

結果:

0

8.線程鎖討論

線程鎖確保某段關鍵代碼只能由一個線程從頭到尾完整執行,但是線程鎖阻止了多線程併發執行,包含鎖的某段代碼實際上只能以單線程模式執行,效率大大降低。其次由於可以存在多個鎖,不同線程支持不同的鎖,試圖獲得對方的鎖時,可能會造成死鎖。

9.全局解釋器鎖(GIL)

任何python線程執行前都必須獲得GIL鎖,執行代碼直到線程睡眠或者python將它掛起,線程釋放GIL。一個處理核心只能在同一時間運行一個線程。Python中可以使用多線程,但不能有效的利用多個處理核心。可以使用多進程來利用多核。

cpu密集型(各種循環,計數等):遇到io操作或者ticks超過100(每執行100條字節碼),釋放GIL。觸發GIL競爭,線程間的來回切換需要消耗資源,故而多線程對CPU密集型代碼並不友好。

io密集型(文件處理/網絡爬蟲):動線程能提升效率(單線程io會進行io等待,造成不必要的時間浪費,開啓多線程可以讓進程A等待時自動切換到線程B,可以不浪費cpu資源,提升效率)

10.爲什麼多進程不會有上述情況。

每一個進程都有各自獨立的GIL,互不影響。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章