1. 程式人生 > 其它 >3.多執行緒.md

3.多執行緒.md

多執行緒

程序與執行緒

  • 程序:指在系統中執行的一個應用程式,程式一旦執行就是程序;

    • 程序:資源分配的最小單位,一個程序至少有一個執行緒
      • 執行緒、記憶體、檔案、網路控制代碼
      • 記憶體:每個程序的記憶體是相互獨立的
      • 檔案/網路控制代碼:他們是所有的程序所共有的,例如開啟同一個檔案,取搶同一個網路埠,這樣的操作是被允許的
      • 搶佔資源,導致死鎖
  • 執行緒:系統分配處理器時間資源的基本單位,或者說程序之內獨立執行的一個單元的執行流。

    • 執行緒:程式執行的最小單位
  • 彙總:

    • 1、程序要分配一大部分的記憶體,而執行緒只需要分配一部分棧就可以了
    • 2、一個程式至少有一個程序,一個程序至少有一個執行緒
    • 3、程序是資源分配的最小單位,執行緒是程式執行的最小單位
    • 4、一個執行緒可以建立和撤銷另一個執行緒,同一個程序中的多個執行緒之間可以併發執行

多執行緒---併發

  • 並行:兩個CPU同時做事情

  • 併發:一個cpu,從執行A任務,接著執行b任務,又執行a任務,接著又執行b任務,迴圈執行

    • 1、使用執行緒可以把佔據長時間的程式中的任務放到後臺去處理
    • 2、使用者介面可以更加吸引人,比如使用者點選了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度
    • 程式的執行速度可能加快
    • 在一些等待的任務實現上如使用者輸入,檔案讀寫和網路收發資料等,執行緒就比較有用了。在這種情況下我們可以釋放一些珍貴的資源如記憶體佔用等等

Threading模組

  • python3執行緒中常用的兩個模組為:

    • _thread
    • threading(推薦使用)
  • thread模組已經被廢棄了。使用者可以使用threading模組代替。所以,在python3中不能再使用thread模組,為了相容性,python3將thread重新命名為_thread

  • 常用方法

    • run():用以表示執行緒活動的方法
    • start():啟動執行緒的方法
    • join([time]):等待至執行緒中止,這阻塞呼叫執行緒直至執行緒的join()方法被呼叫中止--正常退出或者丟擲未處理的異常--或者是可選的超時發生
    • isAlive():返回執行緒是否活動的
    • getName():返回執行緒名
    • setName():設定執行緒名
    • threading.currentThread():返回當前的執行緒變數
    • threading.enumerate():返回一個包含正在執行的執行緒list。正在執行指執行緒啟動後、結束前,不包括啟動前和終止後的執行緒。
    • threading.activeCount():返回正在執行的執行緒數量,與len(threading.enumerate())有相同的效果

多執行緒基礎

import time

def doing(something):
    time.sleep(2)
    print('正在做>>>',something)
start_time =time.time()
doing('在上課')
doing('在上班')
end_time =time.time()
print('總共耗時>>>',end_time-start_time)

控制檯輸出:
正在做>>> 在上課
正在做>>> 在上班
總共耗時>>> 4.010261297225952

io密集型

#-------------------------------------------------------------
"""
    需求:執行效率低
    優化:使用多執行緒
    io密集型
"""
#-------------------------------------------------------------
def doing(something):
    print('正在做>>>', something)
    time.sleep(2)

start_time = time.time()

#1-建立執行緒
"""
    target:函式名
    args:函式名對應的實參,元組形式
"""
t1=threading.Thread(target=doing,args=('在上課',))
t2=threading.Thread(target=doing,args=('在加班',))

#2-啟動執行緒
t1.start()
t2.start()
end_time = time.time()
print('總共耗時>>>', end_time - start_time)

控制檯輸出
正在做>>> 在上課
正在做>>> 在加班總共耗時>>>
 0.0009598731994628906

分析發現跟預期結果不一致,預期結果是大概是2s,現在是0s
原因:直接啟動執行緒:主執行緒(main)不等待子執行緒(t1/t2)完成就結束

優化方案:

3-阻塞主執行緒

t1.join()
t2.join() 

控制檯輸出
正在做>>> 在上課
正在做>>> 在加班
總共耗時>>> 2.0047731399536133

計算密集型

#-------------------------------------------------------------
"""
    需求:執行效率低
    優化:使用多執行緒
    計算密集型
"""
#-------------------------------------------------------------
def doing():
    dataNum=0
    for i in range(10000000):
        dataNum+=1

start_time = time.time()

#1-建立執行緒
"""
    target:你這個執行緒是做什麼,需要執行的函式名
    args:函式名對應的實參,元組形式
    直接啟動執行緒:主執行緒(main)不等待子執行緒(t1/t2)完成就結束
    需求:主執行緒退出之前需要等待子執行緒全部執行完
    優化:阻塞主執行緒 
    序列:總共耗時>>> 1.0268769264221191
"""
#t1=threading.Thread(target=doing,args=('在上課',))
#t2=threading.Thread(target=doing,args=('在加班',))
#
##2-啟動執行緒
#t1.start()
#t2.start()
##3-阻塞主執行緒
#t1.join()
#t2.join()
doing()
doing()
end_time = time.time()
print('總共耗時>>>', end_time - start_time)

控制檯輸出:
總共耗時>>> 1.0268769264221191

多執行緒方式;
改變部分的程式碼

t1=threading.Thread(target=doing)
t2=threading.Thread(target=doing)

#2-啟動執行緒
t1.start()
t2.start()
#3-阻塞主執行緒
t1.join()
t2.join()

控制檯輸出
總共耗時>>> 1.0593979358673096

通過對比發現對應計算密集型來說,使用序列和多執行緒,耗時一樣
對於cpython直譯器GIL(全域性直譯器鎖)),不管多少核 cpu同一時間只能處理一件事

原因:
多執行緒是併發:併發是來回切換執行不同的任務,導致計算密集型執行的時間比序列還長,因為來回切換也需要耗時

守護執行緒:

#-------------------------------------------------------------
"""
    需求:執行效率低
    優化:使用多執行緒
    守護執行緒
    主執行緒想滿足一個條件就退出,使用多執行緒直接不能直接退出主執行緒
"""
#-------------------------------------------------------------
def doing():
    while True:
        print('我在doing')
        time.sleep(1)

start_time = time.time()

#1-建立執行緒
"""
    target:你這個執行緒是做什麼,需要執行的函式名
    args:函式名對應的實參,元組形式
    直接啟動執行緒:主執行緒(main)不等待子執行緒(t1/t2)完成就結束
    需求:主執行緒退出之前需要等待子執行緒全部執行完
    優化:阻塞主執行緒 
    序列:總共耗時>>> 1.0268769264221191
"""
t1=threading.Thread(target=doing)
t2=threading.Thread(target=doing)

#2-啟動執行緒
t1.start()
t2.start()
#3-阻塞主執行緒
#t1.join()
#t2.join()
end_time = time.time()
for i in range(3):
    print('**********主執行緒正在執行*******')
print('**********主執行緒結束*******')
print('總共耗時>>>', end_time - start_time)


控制檯輸出:
死迴圈
我在doing
我在doing**********主執行緒正在執行*******

**********主執行緒正在執行*******
**********主執行緒正在執行*******
**********主執行緒結束*******
總共耗時>>> 0.0009987354278564453
我在doing我在doing

我在doing我在doing

我在doing我在doing

我在doing我在doing

我在doing我在doing

分析:
主執行緒一直無法退出
優化:
增加守護執行緒 setdaemon(True)
在以下位置增加守護部分程式碼

t1=threading.Thread(target=doing)
t2=threading.Thread(target=doing)

t1.setDaemon(True)#守護
t2.setDaemon(True)

#2-啟動執行緒
t1.start()
t2.start()

控制檯輸出
我在doing
我在doing
**********主執行緒正在執行*******
**********主執行緒正在執行*******
**********主執行緒正在執行*******
**********主執行緒結束*******
總共耗時>>> 0.0009744167327880859