前言:前面講到了多程序,那麼本部落客今天來深剖實作多任務的第二種方式:
線程(threading)
文章目錄:
- 一.引入線程:
- 二.普通程式與線程:.
-
- 1.普通程式舉例:
- 2.線程執行:
- 三.線程(threading):
-
- 1.threading.Thread():
- 2.start():
- 3.join():
- 四.檢視線程數量:
- 五.線程特點:
一.引入線程:
衆所周知傳統的程序有兩個基本屬性:
- 可擁有資源的獨立機關;
- 可獨立排程和配置設定的基本機關;
引入線程的基本原因是,程序在
建立
、·切換·和·撤銷·時,系統必須為之付出·較大的記憶體開銷·,是以在系統中設定的
程序數量不宜過多
,程序的
切換頻率不宜太高
,這就·限制·了·并發程度·的提高。引入線程後,将傳統程序的兩個基本屬性分開,
線程作為排程和配置設定的基本機關
,進
程作為獨立配置設定資源的機關
。使用者可以建立線程來完成任務,以減少程式并發執行時付出的記憶體開銷。
- 程序給線程目标,線程去完成這個目标,
- 一個程式執行必然有一個線程(也就是主線程),一個程式執行必然有一個程序(主程序)。
二.普通程式與線程:.
1.普通程式舉例:
假定我們在執行程式時,會構造很多函數,但是如果每個函數都要完成自己的事情,在普通程式中,隻有當一個完成時才會執行另一個函數,這樣子會造成記憶體過度開銷,例如:
import time
def run():
for _ in range(5):
time.sleep(1)
print("我會跑")
def sing():
for _ in range(5):
time.sleep(1)
print("我會唱歌")
if __name__ == '__main__':
run()
sing()
我們簡單的定義了兩個函數,各有各的事情要做,但是這種情況隻有當
run()
函數執行完才會去執行
sing()
函數,這樣程式會一直等待,會造成
記憶體
不必要的
開銷
,結果如下圖:
2.線程執行:
線程引入需要導包,也就是
threading
這個包,
thread
子產品是比較底層的子產品,
threading
是對
thread
的
包裝
。是以導入
threading
,我們先來看一下差別,然後我們來看看線程的聲明以及開啟;
import threading
import time
def run():
for _ in range(5):
time.sleep(1)
print("我會跑")
def sing():
for _ in range(5):
time.sleep(1)
print("我會唱歌")
if __name__ == '__main__':
run_thread=threading.Thread(target=run)
sing_thread=threading.Thread(target=sing)
run_thread.start()
sing_thread.start()
三.線程(threading):
通過上面的舉例我們都已經看到了兩者的差別,線程會各幹各的事,節約了不少記憶體空間,但是怎麼使用呢--------------------------我們來看看:
1.threading.Thread():
和程序一樣建立一個
Thread對象
,來進行
建立這個線程
,
Thread
的參數有:
- group:指定程序組,一般就預設為
;None
- target:執行目标任務名;
- name:程序名字;
- args:以元組形式向執行任務傳參;
- kwargs:以字典形式向執行任務傳參.
- daemon:守護程式,預設值為
;None
舉例:
run_thread=threading.Thread(target=run)
sing_thread=threading.Thread(target=sing)
在前面進行定義的方法就可以在這兒通過此方法進行建立哦
2.start():
和程序中一樣都是用來開啟程序的,例如我們在面建立了兩個線程,那麼在此就可以進行開啟:
run_thread.start()
sing_thread.start()
3.join():
當主線程執行完再執行子線程,和程序一樣:
run_thread.start()
run_thread.join()
sing_thread.start()
四.檢視線程數量:
當線程有點多的時候,我們可以通過
enumerate()
來進行檢視線程的數量,如下:
import threading
import time
count=0
def run():
for _ in range(5):
time.sleep(1)
print("我會跑")
print(threading.enumerate())
def sing():
for _ in range(5):
time.sleep(1)
print("我會唱歌")
print(threading.enumerate())
if __name__ == '__main__':
# run()
# sing()
run_thread=threading.Thread(target=run)
sing_thread=threading.Thread(target=sing)
run_thread.start()
sing_thread.start()
print(threading.enumerate())
我們可以看到,圖中總共有三個線程,一個主線程,兩個子線程,并且有
Thread-x
來進行辨別
五.線程特點:
-
線程執行代碼的封裝,通過threading這個包中的Thread功能,往往會定義一個新的子類Class來繼承:threading.thread,一般情況下線程的主入口函數為run(),我們隻不過對其進行重寫而已:
import threading
class threa(threading.Thread):
def run(self):
for _ in range(5):
time.sleep(1)
print("我會跑")
print(threading.enumerate())
if __name__ == '__main__':
# run()
# sing()
a=threa()
run_thread=threading.Thread(target=a.run)
run_thread.start()
ret=len(threading.enumerate())
print(ret)
-
多線程共享全局變量:
對全局變量進行修改時,判斷是否對全局變量進行引用修改,如果修改了引用然後執行,讓全局變量指向了另一個新地方,如果修改了引用的資料,則不必擔心變量被分化:
舉例:
import threading
import time
count=0
def sum():
for i in range(10000):
global count
count+=1
print(f"我是第一個:{count}")
def sum1():
for i in range(10000):
global count
count+=1
print(f"我是第二個:{count}")
if __name__ == '__main__':
# run()
# sing()
sum_thread=threading.Thread(target=sum)
sum1_thread = threading.Thread(target=sum1)
sum_thread.start()
sum1_thread.start()
# run_thread.start()
# run_thread.join()
# sing_thread.start()
print(f"總計為:{count}")
由上圖可以看出,線程是
共享全局變量
的,但是修改全局變量需用
globa
l哦