沙師弟學Python多任務之線程

  • 什麼叫多任務呢?
    • 在現實生活中,有不少場景是同時進行的,好比唱歌跳舞,試想若是把這二者分開來,該有多滑稽。
    • 多任務其實就是操做系統同時處理多個任務,處理多任務得有多核處理器,因此在多核處理器普及以後,真正的多任務才實現。
  • 多任務能夠分爲併發並行,其中併發是「假多任務」,即單核處理器在極短的時間內循環處理多個任務,極短的時間給人形成一種「多任務」的錯覺;而並行是「真多任務」,即每一個核處理一個任務。
  • Python的多任務實現方式可分爲「進程」,「線程」,「協程」,其中進程佔用資源最大,可是穩定,線程佔用資源和穩定性處於三者中間,協程是輕量級的,穩定性也較差一點。

線程

什麼是「線程」呢?線程能夠理解爲程序中能夠執行的一個分支。程序默認是有一個線程的,稱爲「主線程」,這個線程負責從上至下執行程序。在從上至下執行的過程當中,有時候須要程序裏面的多個函數一塊兒執行,這時候就須要用到「多線程」。python

如何建立多線程

Python建立多線程可使用threading模塊,下面看看我是怎麼實現一下邊唱歌邊跳舞的:多線程

import time
import threading

def sing():
    for i in range(5):
        print("sing")
        time.sleep(1)
        
def dance():
    for i in range(5):
        print("dance")
        time.sleep(1)
            
def main():
    t1 = threading.Thread(target=sing) # 建立子線程
    t2 = threading.Thread(target=dance)
    t1.start() # 子線程開始執行
    t2.start()
        
if __name__ == "__main__":
    main()
複製代碼

注意:併發

  1. 建立子線程的的時候 target 傳入的是目標函數的名稱,是 不帶括號 的;
  2. 線程運行起來是 無序; 的,是由操做系統決定的,主線程須等待子線程都執行完成之後才退出程序。

查看運行中的線程

threading裏面有個enumerate()方法能夠查看當前有哪些線程中運行,代碼以下:函數

import threading
import time

def test1():
    for i in range(5):
        print("-----test1-----%d---" % i)
        time.sleep(1)

def test2():
    for i in range(5):
        print("-----test2-----%d---" % i)
        time.sleep(1)
        
def main():
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)
    
    t1.start()
    time.sleep(1)
    
    t2.start()
    time.sleep(1)
    
    while True:
        print(threading.enumerate)
        time.sleep(1)
if __name__ == "__main__":
    main()
複製代碼

多線程之間是共享全局變量的,但多線程中共享全局變量有個缺點是會出現競爭,如何解決資源競爭呢,這就涉及到「互斥鎖」。ui

互斥鎖

互斥鎖的出現是爲了解決幾個線程之間產生的資源競爭,當多個線程須要同時進行一個全局變量進行處理的時候,互斥鎖就派上用場了。這就跟上廁所關門同樣,你關上門以後就不容許其餘人進入這個廁所。 某個線程要更改共享數據時,先將其鎖定,此時資源的狀態爲「鎖定」,其餘線程不能修改,直到該線程釋放資源,資源的狀態變爲「非鎖定」,其餘的線程才能再次鎖定該資源。spa

import threading
import time

# 定義一個全局變量
g_num = 0


def test1(num):
    global g_num
    # 通常上鎖會上在代碼儘可能少的地方,省得形成一個程序上鎖以後長時間不解鎖的狀況
    for i in range(num):
        # 上鎖,若是以前沒有被上鎖,那麼此時上鎖成功,
        # 若是以前已經被上鎖,那麼此時被堵塞,知道鎖被解開
        mutex.acquire()
        g_num += 1
        # 解鎖
        mutex.release()
    print("-------in test1 g_num=%d-----" % g_num)


def test2(num):
    global g_num
    for i in range(num):
        mutex.acquire()
        g_num += 1
        # 解鎖
        mutex.release()
    print("-------in test2 g_num=%d-----" % g_num)


# 建立一個互斥鎖,默認沒有上鎖
mutex = threading.Lock()


def main():
    t1 = threading.Thread(target=test1, args=(1000000,))
    t2 = threading.Thread(target=test2, args=(1000000,))

    t1.start()
    t2.start()

    time.sleep(5)

    print("-------in main Thread g_num = %d----" % g_num)


if __name__ == '__main__':
    main()
複製代碼

結果操作系統

-------in main Thread g_num = 933690----
-------in test1 g_num=1999178-----
-------in test2 g_num=2000000-----
複製代碼

爲何主線程會出如今子線程以前呢,這個是由於這5秒以內子線程沒有完成計算形成的。線程

總結

線程是在「進程」,「線程」,「協程」之間處於「老二」的地位的,通常在咱們寫程序裏面仍是常常用到線程的,好比音樂播放器的邊播放和邊下載。code

相關文章
相關標籤/搜索