Python多線程的那些事兒

對於多線程的理解,通俗的講就是程序在同一時間執行多個任務。與多線程相對的就是單線程,同一時間只做一件事,要麼是聽音樂,要麼是看電影;而多線程則是可以一邊聽音樂,又可以一邊看電影。

單線程

首先舉一個單線程的例子:

# example 1

from time import ctime, sleep

def music():
    for i in range(2):
        print('Start Listening Music: %s' % ctime())
        sleep(1)
        print('Finish Listening Music: %s' % ctime())

def movie():
    for i in range(2):
        print('Start Watching Movie: %s' % ctime())
        sleep(5)
        print('Finish Watching Movie: %s' % ctime())

if __name__ == '__main__':
    music()
    movie()
    print('All spent: %s' % ctime())
# result 1
Start Listening Music: Mon Aug 26 19:22:21 2019
Finish Listening Music: Mon Aug 26 19:22:22 2019
Start Listening Music: Mon Aug 26 19:22:22 2019
Finish Listening Music: Mon Aug 26 19:22:23 2019
Start Watching Movie: Mon Aug 26 19:22:23 2019
Finish Watching Movie: Mon Aug 26 19:22:28 2019
Start Watching Movie: Mon Aug 26 19:22:28 2019
Finish Watching Movie: Mon Aug 26 19:22:33 2019
All spent: Mon Aug 26 19:22:33 2019

上面這個例子是聽音樂和看電影分開處理的,先聽音樂,聽了兩首,每首1秒鐘;然後是看電影,看了兩部,每部5秒鐘。程序的總共執行了12秒。

多線程

多線程就是將聽音樂和看電影同時進行。在Python中多線程使用thread和threading模塊。thread模塊提供了基本的線程和鎖定支持,而threading模塊提供了更高級別,功能更全面的線程管理。

"更高級別的threading模塊更爲先進,對線程支持更爲完善,
而且使用thread模塊裏的屬性有可能會與threading出現衝突。
其次,低級別的thread模塊的同步原語很少(實際上只有一個),而threading模塊則有很多。"

                                                                                                                                                       ——《Python核心編程》

所以在這裏推薦使用threading模塊來進行python多線程。

在threading模塊中主要提供了一下方法:

方法與屬性 描述
current_thread() 返回當前線程
active_count() 返回當前活躍的線程數,1個主線程+n個子線程
get_ident() 返回當前線程
enumerater() 返回當前活動 Thread 對象列表
main_thread() 返回主 Thread 對象
settrace(func) 爲所有線程設置一個 trace 函數
setprofile(func) 爲所有線程設置一個 profile 函數
stack_size([size]) 返回新創建線程棧大小;或爲後續創建的線程設定棧大小爲 size
TIMEOUT_MAX Lock.acquire(), RLock.acquire(), Condition.wait() 允許的最大超時時間

threading.Thread類

對於執行多線程,使用的是Thread類,它的定義如下:

threading.Thread(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
  • 參數group是預留的,用於將來擴展;
  • 參數target是一個可調用對象,在線程啓動後執行;
  • 參數name是線程的名字。默認值爲“Thread-N“,N是一個數字;
  • 參數args和kwargs分別表示調用target時的參數列表和關鍵字參數;
  • daemon表示是否是守護線程,默認爲False,守護線程是指當主線程結束後該子線程也會隨之結束,是主線程的守護。

下面是執行多線程的一個例子:

# example 2
import threading
from time import ctime, sleep

def music():
    for i in range(2):
        print('%s Start Listening Music: %s' % (threading.current_thread().getName(), ctime()))
        sleep(1)
        print('%s Finish Listening Music: %s' % (threading.current_thread().getName(), ctime()))


def movie():
    for i in range(2):
        print('%s Start Watching Movie: %s' % (threading.current_thread().getName(), ctime()))
        sleep(5)
        print('%s Finish Watching Movie: %s' % (threading.current_thread().getName(), ctime()))


if __name__ == '__main__':
    threads = []
    t1 = threading.Thread(target=music, daemon=True)
    t2 = threading.Thread(target=movie, daemon=True)
    threads.append(t1)
    threads.append(t2)

    for t in threads:
        t.start()
    print('All spent: %s' % ctime())
# result 2
Thread-1 Start Listening Music: Mon Aug 26 19:40:49 2019
Thread-2 Start Watching Movie: Mon Aug 26 19:40:49 2019
All spent: Mon Aug 26 19:40:49 2019

從上面的結果可以看到,這並不是我們想要的結果,因爲Thread-1和Thread-2纔剛執行還沒執行完畢程序就已經終止。這是因爲兩個子線程的daemon屬性都設置成True,成爲主線程的守護線程。那麼我們將daemon屬性設置成False能否得到想要的結果呢?

daemon屬性

# example 3
import threading
from time import ctime, sleep

def music():
    for i in range(2):
        print('%s Start Listening Music: %s' % (threading.current_thread().getName(), ctime()))
        sleep(1)
        print('%s Finish Listening Music: %s' % (threading.current_thread().getName(), ctime()))


def movie():
    for i in range(2):
        print('%s Start Watching Movie: %s' % (threading.current_thread().getName(), ctime()))
        sleep(5)
        print('%s Finish Watching Movie: %s' % (threading.current_thread().getName(), ctime()))


if __name__ == '__main__':
    threads = []
    t1 = threading.Thread(target=music)
    t2 = threading.Thread(target=movie)
    threads.append(t1)
    threads.append(t2)

    for t in threads:
        t.start()
    print('All spent: %s' % ctime())
# result 3
Thread-1 Start Listening Music: Mon Aug 26 19:47:05 2019
Thread-2 Start Watching Movie: Mon Aug 26 19:47:05 2019
All spent: Mon Aug 26 19:47:05 2019
Thread-1 Finish Listening Music: Mon Aug 26 19:47:06 2019
Thread-1 Start Listening Music: Mon Aug 26 19:47:06 2019
Thread-1 Finish Listening Music: Mon Aug 26 19:47:07 2019
Thread-2 Finish Watching Movie: Mon Aug 26 19:47:10 2019
Thread-2 Start Watching Movie: Mon Aug 26 19:47:10 2019
Thread-2 Finish Watching Movie: Mon Aug 26 19:47:15 2019

從結果中可以看到這並不是我們想要的結果。雖然子線程Thread-1和Thread-2同時開始,但是子線程還沒結束主線程(All spent)已經結束,我們想要的是主線程在所有子線程結束後才結束。這就需要用到join()方法。

join()方法

join()方法的作用是在子線程完成運行之前,這個子線程的父線程將一直被阻塞。簡而言之就是希望主線程等等子線程,直到子線程執行完畢。

# example 4
import threading
from time import ctime, sleep

def music():
    for i in range(2):
        print('%s Start Listening Music: %s' % (threading.current_thread().getName(), ctime()))
        sleep(1)
        print('%s Finish Listening Music: %s' % (threading.current_thread().getName(), ctime()))

def movie():
    for i in range(2):
        print('%s Start Watching Movie: %s' % (threading.current_thread().getName(), ctime()))
        sleep(5)
        print('%s Finish Watching Movie: %s' % (threading.current_thread().getName(), ctime()))

if __name__ == '__main__':
    threads = []
    t1 = threading.Thread(target=music, daemon=True)
    t2 = threading.Thread(target=movie, daemon=True)
    threads.append(t1)
    threads.append(t2)

    for t in threads:
        t.start()
    t.join()
    print('All spent: %s' % ctime())
# result 4
Thread-1 Start Listening Music: Mon Aug 26 19:52:25 2019
Thread-2 Start Watching Movie: Mon Aug 26 19:52:25 2019
Thread-1 Finish Listening Music: Mon Aug 26 19:52:26 2019
Thread-1 Start Listening Music: Mon Aug 26 19:52:26 2019
Thread-1 Finish Listening Music: Mon Aug 26 19:52:27 2019
Thread-2 Finish Watching Movie: Mon Aug 26 19:52:30 2019
Thread-2 Start Watching Movie: Mon Aug 26 19:52:30 2019
Thread-2 Finish Watching Movie: Mon Aug 26 19:52:35 2019
All spent: Mon Aug 26 19:52:35 2019

可以看到整個過程用了10s,比單線程快了2s,這是因爲看電影用了10s,在看電影的過程中聽音樂已經結束,等電影結束後主進程才結束。如果將看電影調整爲4s呢?

# example 5
import threading
from time import ctime, sleep

def music():
    for i in range(2):
        print('%s Start Listening Music: %s' % (threading.current_thread().getName(), ctime()))
        sleep(4)
        print('%s Finish Listening Music: %s' % (threading.current_thread().getName(), ctime()))

def movie():
    for i in range(2):
        print('%s Start Watching Movie: %s' % (threading.current_thread().getName(), ctime()))
        sleep(5)
        print('%s Finish Watching Movie: %s' % (threading.current_thread().getName(), ctime()))

if __name__ == '__main__':
    threads = []
    t1 = threading.Thread(target=music, daemon=True)
    t2 = threading.Thread(target=movie, daemon=True)
    threads.append(t1)
    threads.append(t2)

    for t in threads:
        t.start()
    print('Start Main Thread: %s' % ctime())
    sleep(2)
    t.join()
    print('Finish Main Thread: %s' % ctime())
    print('All spent: %s' % ctime())
# result 5
Thread-1 Start Listening Music: Mon Aug 26 19:55:35 2019
Thread-2 Start Watching Movie: Mon Aug 26 19:55:35 2019
Start Main Thread: Mon Aug 26 19:55:35 2019
Thread-1 Finish Listening Music: Mon Aug 26 19:55:39 2019
Thread-1 Start Listening Music: Mon Aug 26 19:55:39 2019
Thread-2 Finish Watching Movie: Mon Aug 26 19:55:40 2019
Thread-2 Start Watching Movie: Mon Aug 26 19:55:40 2019
Thread-1 Finish Listening Music: Mon Aug 26 19:55:43 2019
Thread-2 Finish Watching Movie: Mon Aug 26 19:55:45 2019
Finish Main Thread: Mon Aug 26 19:55:45 2019
All spent: Mon Aug 26 19:55:45 2019

可以看到雖然聽音樂增加到了4s,但是總過程還是10s,這是取決於兩個子線程時間最長的那一個,即看電影。

從上面的例子中可以總結出,daemon是將該子線程設置爲守護線程,join()方法的作用是阻塞主線程直到子線程結束。

自定義線程

自定義線程的步驟:

(1)繼承Thread類
(2)重寫__init__方法添加自己的屬性,使用super加載父類屬性
(3)重寫run方法

import threading

class MyThreading(threading.Thread):

    def __init__(self, func, arg):
        super(MyThreading,self).__init__()
        self.func = func
        self.arg = arg

    def run(self):
        self.func(self.arg)

def my_func(args):
    """
    to do
    """
    pass

obj = MyThreading(my_func, 123)
obj.start()

線程鎖

多個進程同時進行的時候呢,若同時對一個變量訪問,那就會出現混亂,具體的混亂可以看下面的例子:

# example 6
import threading
import time

number = 0

def plus():
    global number  # global聲明此處的number是外面的全局變量number
    print("Start %s, time = %s" % (threading.current_thread().getName(), time.ctime()))
    for _ in range(1000000):  
        number += 1
    print("Finish %s, number = %s, time = %s" % (threading.current_thread().getName(), number, time.ctime()))

print('Start: %s' % time.ctime())
for i in range(2):  
    t = threading.Thread(target=plus)
    t.start()

time.sleep(2)  # 等待2秒,確保2個子線程都已經結束運算。
print("Finish Main Thread, number = ", number)
# result 6
Start: Mon Aug 26 20:08:39 2019
Start Thread-1, time = Mon Aug 26 20:08:39 2019
Start Thread-2, time = Mon Aug 26 20:08:39 2019
Finish Thread-2, number = 1132339, time = Mon Aug 26 20:08:39 2019
Finish Thread-1, number = 1144554, time = Mon Aug 26 20:08:39 2019
Finish Main Thread, number =  1144554

程序主要是從0每次加1加到999999,理想結果應該是每個線程爲1000000,最後的輸出結果是2000000。而運行的結果卻是1144554,說明多線程對單變量的訪問出現了混亂,並且這種混亂是每次執行的結果都不一樣。那麼如何保證在某一時刻只有一個線程可以修改變量呢?這就需要引入線程鎖。

threading模塊的線程鎖主要有一下幾種:

  • Lock 互斥鎖
  • RLock 可重入鎖
  • Semaphore 信號
  • Event 事件
  • Condition 條件
  • Barrier “阻礙”

Lock互斥鎖

互斥鎖是指同一時刻只有一個線程可以訪問共享的數據。首先初始化鎖對象,然後將鎖當做參數傳遞給任務函數,在任務中加鎖,使用後釋放鎖。

# example 7
import threading
import time

def plus(lk):
    global number  # global聲明此處的number是外面的全局變量number
    lk.acquire()  # 開始加鎖
    print("Start %s, time = %s" % (threading.current_thread().getName(), time.ctime()))
    for _ in range(10000000):  
        number += 1
    print("Finish %s, number = %s, time = %s" % (threading.current_thread().getName(), number, time.ctime()))
    lk.release()  # 釋放鎖,讓別的線程也可以訪問number

if __name__ == '__main__':
    number = 0
    lock = threading.Lock()
    print('Start: %s' % time.ctime())
    for i in range(2):  
        t = threading.Thread(target=plus, args=(lock,))  
        t.start()
    t.join()  
    print("Finish Main Thread, number = ", number)
# result 7
Start: Mon Aug 26 20:24:22 2019
Start Thread-1, time = Mon Aug 26 20:24:22 2019
Finish Thread-1, number = 10000000, time = Mon Aug 26 20:24:24 2019
Start Thread-2, time = Mon Aug 26 20:24:24 2019
Finish Thread-2, number = 20000000, time = Mon Aug 26 20:24:26 2019
Finish Main Thread, number =  20000000

Semaphore信號

信號的用法跟互斥鎖非常的相似,原理與線程鎖並沒有多大的區別,只不過線程鎖鎖住一個線程在運行和修改數據,而信號量可以自己控制同一時刻運行幾個線程和幾個線程修改數據,也就是設置最大同時運行的線程數.

# example 8
import time
import threading

def run(n, se):
    se.acquire()
    print("%s run the thread: %s" % (threading.current_thread().getName(), n))
    time.sleep(1)
    se.release()

# 設置允許5個線程同時運行
semaphore = threading.BoundedSemaphore(5)
for i in range(20):
    t = threading.Thread(target=run, args=(i, semaphore))
    t.start()
# result 8
Thread-1 run the thread: 0
Thread-2 run the thread: 1
Thread-3 run the thread: 2
Thread-4 run the thread: 3
Thread-5 run the thread: 4
Thread-6 run the thread: 5
Thread-8 run the thread: 7
Thread-7 run the thread: 6
Thread-9 run the thread: 8
Thread-10 run the thread: 9
Thread-11 run the thread: 10
Thread-12 run the thread: 11
Thread-13 run the thread: 12
Thread-14 run the thread: 13
Thread-15 run the thread: 14
Thread-16 run the thread: 15
Thread-18 run the thread: 17
Thread-17 run the thread: 16
Thread-19 run the thread: 18
Thread-20 run the thread: 19

Event信號

使用方法:e = threading.Event()

Event對象主要用於線程間通信,確切地說是用於主線程控制其他線程的執行。

Event事件提供了四個方法:wait()、clear()、set()和is_set()。

Event事件實現通信機制:全局定義了一個“Flag”(默認爲False),若調用clear()則Flag爲False,執行event.wait方法時會阻塞;調用set()則Flag爲True,則執行event.wait方法時便不阻塞,判斷Flag狀態用is_set()。默認Flag爲False時會阻塞。

下面使用Event時間模擬信號燈的一個程序,紅燈綠燈時間均爲5s,若紅燈亮,則車輛出於等待狀態,若綠燈亮,則車輛行駛。

# example 9
# 利用Event類模擬紅綠燈
import threading
import time

event = threading.Event()


def lighter():
    green_time = 5  # 綠燈時間
    red_time = 5  # 紅燈時間
    event.set()  # 初始設爲綠燈
    while True:
        print("\33[32;0m 綠燈亮...\033[0m")
        time.sleep(green_time)
        event.clear()
        print("\33[31;0m 紅燈亮...\033[0m")
        time.sleep(red_time)
        event.set()


def run(name):
    while True:
        if event.is_set():  # 判斷當前是否"放行"狀態
            print("一輛[%s] 呼嘯開過..." % name)
            time.sleep(1)
        else:
            print("一輛[%s]開來,看到紅燈,無奈的停下了..." % name)
            event.wait()
            print("[%s] 看到綠燈亮了,瞬間飛起....." % name)  # 等待信號燈爲True後執行


if __name__ == '__main__':
    timer = threading.Timer(15, lighter)
    timer.start()
    light = threading.Thread(target=lighter, )
    light.start()

    for name in ['奔馳', '寶馬', '奧迪']:
        car = threading.Thread(target=run, args=(name,))
        car.start()
    timer.cancel()
# result 9
 綠燈亮...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
 紅燈亮...
一輛[奔馳]開來,看到紅燈,無奈的停下了...
一輛[寶馬]開來,看到紅燈,無奈的停下了...
一輛[奧迪]開來,看到紅燈,無奈的停下了...
 綠燈亮...
[奔馳] 看到綠燈亮了,瞬間飛起.....
一輛[奔馳] 呼嘯開過...
[奧迪] 看到綠燈亮了,瞬間飛起.....
一輛[奧迪] 呼嘯開過...
[寶馬] 看到綠燈亮了,瞬間飛起.....
一輛[寶馬] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
 紅燈亮...
一輛[寶馬]開來,看到紅燈,無奈的停下了...
一輛[奔馳]開來,看到紅燈,無奈的停下了...
一輛[奧迪]開來,看到紅燈,無奈的停下了...
 綠燈亮...
[寶馬] 看到綠燈亮了,瞬間飛起.....
[奔馳] 看到綠燈亮了,瞬間飛起.....
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
[奧迪] 看到綠燈亮了,瞬間飛起.....
一輛[奧迪] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[奔馳] 呼嘯開過...
一輛[奧迪] 呼嘯開過...
一輛[寶馬] 呼嘯開過...
 紅燈亮...
一輛[寶馬]開來,看到紅燈,無奈的停下了...
一輛[奔馳]開來,看到紅燈,無奈的停下了...
一輛[奧迪]開來,看到紅燈,無奈的停下了...

Condition條件變量

條件變量是指當某一線程滿足要求後,其他線程才能執行。

比如喫火鍋的時候,有放魚丸的人和喫魚丸的人,當放魚丸達到一定數量的時候(比如5個),然後通知大家來喫魚丸。如果魚丸喫完了,那麼就會通知放魚丸的人來放魚丸。當放了5個的時候又通知其他人喫魚丸...這是一個生產者和消費者行爲。

Condition(條件變量)通常與一個鎖關聯。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構造方法,否則它將自己生成一個RLock實例。

可以認爲,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處於狀態圖中的等待阻塞狀態,直到另一個線程調用notify()/notifyAll()通知;得到通知後線程進入鎖定池等待鎖定。

Condition():

  • acquire(): 線程鎖
  • release(): 釋放鎖
  • wait(timeout): 線程掛起,直到收到一個notify通知或者超時(可選的,浮點數,單位是秒s)纔會被喚醒繼續運行。wait()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError。
  • notify(n=1): 通知其他線程,那些掛起的線程接到這個通知之後會開始運行,默認是通知一個正等待該condition的線程,最多則喚醒n個等待的線程。notify()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError。notify()不會主動釋放Lock。
  • notifyAll(): 如果wait狀態線程比較多,notifyAll的作用就是通知所有線程。
# example 10
import threading
import time

con = threading.Condition()
num = 0


# 生產者
class Producer(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        # 鎖定線程
        global num
        con.acquire()
        while True:
            if num == 0:
                print("開始添加!!!")
            num += 1
            print("火鍋裏面魚丸個數:%s" % str(num))
            time.sleep(1)
            if num >= 5:
                print("火鍋裏面裏面魚丸數量已經到達5個,無法添加了!")
                # 喚醒等待的線程
                con.notify()  # 喚醒小夥伴開喫啦
                # 等待通知
                con.wait()
        # 釋放鎖
        con.release()


# 消費者
class Consumers(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        con.acquire()
        global num
        while True:
            if num == 5:
                print("開始喫啦!!!")
            num -= 1
            print("火鍋裏面剩餘魚丸數量:%s" % str(num))

            time.sleep(2)
            if num <= 0:
                print("鍋底沒貨了,趕緊加魚丸吧!")
                con.notify()  # 喚醒其它線程
                # 等待通知
                con.wait()
        con.release()


p = Producer()
c = Consumers()
p.start()
c.start()
# result 10
開始添加!!!
火鍋裏面魚丸個數:1
火鍋裏面魚丸個數:2
火鍋裏面魚丸個數:3
火鍋裏面魚丸個數:4
火鍋裏面魚丸個數:5
火鍋裏面裏面魚丸數量已經到達5個,無法添加了!
開始喫啦!!!
火鍋裏面剩餘魚丸數量:4
火鍋裏面剩餘魚丸數量:3
火鍋裏面剩餘魚丸數量:2
火鍋裏面剩餘魚丸數量:1
火鍋裏面剩餘魚丸數量:0
鍋底沒貨了,趕緊加魚丸吧!
開始添加!!!
火鍋裏面魚丸個數:1
火鍋裏面魚丸個數:2
火鍋裏面魚丸個數:3
火鍋裏面魚丸個數:4
火鍋裏面魚丸個數:5
火鍋裏面裏面魚丸數量已經到達5個,無法添加了!
開始喫啦!!!
火鍋裏面剩餘魚丸數量:4
火鍋裏面剩餘魚丸數量:3
火鍋裏面剩餘魚丸數量:2
火鍋裏面剩餘魚丸數量:1
火鍋裏面剩餘魚丸數量:0
鍋底沒貨了,趕緊加魚丸吧!

類似的其他例子:https://blog.csdn.net/comprel/article/details/72798407

Timer定時器

指定n秒s執行某個操作

from threading import Timer

def hello():
    print("hello, world")

# 表示1秒後執行hello函數
t = Timer(1, hello)
t.start()

Queue實現結果存儲

由於多線程中沒有返回值,若有些函數需要return返回值該怎麼辦呢?那就需要導入queue模塊來存儲各線程的返回值。

# example 11
import threading
from queue import Queue


def job(l, q):
    for i in range(len(l)):
        l[i] = l[i] ** 2
    q.put(l)
    # return l    # 本來這裏應該是將列表l返回的,現在存儲在隊列中


if __name__ == '__main__':
    q = Queue()
    threads = []
    data = [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]

    for i in range(len(data)):
        t = threading.Thread(target=job, args=(data[i], q))
        t.start()
        threads.append(t)
    for thread in threads:
        thread.join()

    result = []
    for _ in range(len(data)):
        result.append(q.get())
    print(result)
# result 11
[[1, 4, 9], [4, 9, 16], [9, 16, 25], [16, 25, 36]]

with語句

鎖的鎖定與釋放與文件的打開與關閉類似,若打開了鎖未能正確關閉則會影響程序的進行,因此同樣可以用with語句來自動關閉鎖:

with some_lock:
    # 執行任務...

等同於:

some_lock.acquire()
try:
    # 執行任務..
finally:
    some_lock.release()

全局解釋器鎖GIL(Global Interpreter Lock)

python在設計之初就考慮到在解釋器的主循環中,同時只有一個線程在運行。即在任意時刻只有一個線程在解釋器中運行。對python虛擬機訪問的控制由全局解釋鎖GIL控制,正是這個鎖來控制同一時刻只有一個線程能夠運行。

詳細參考:

python之理解GIL

Python的GIL是什麼鬼,多線程性能究竟如何

 

 

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