1. 程式人生 > >線程 threading模塊

線程 threading模塊

分享圖片 start 高效率 tpi end while 整體 利用 setname

線程

什麽是線程

  • 線程是計算機中被cpu調度的最小單位
  • cpu都是執行的線程中的代碼
  • 線程被包含在進程中 ,是進程的實際運作單位
60年代,在OS中能擁有資源和獨立運行的基本單位是進程,然而隨著計算機技術的發展,進程出現了很多弊端,一是由於進程是資源擁有者,創建、撤消與切換存在較大的時空開銷,因此需要引入輕型進程;二是由於對稱多處理機(SMP)出現,可以滿足多個運行單位,而多個進程並行開銷過大。   因此在80年代,出現了能獨立運行的基本單位——線程(Threads)   註意:進程是資源分配的最小單位,線程是CPU調度的最小單位.      每一個進程中至少有一個線程。 

線程和進程之間的關系

觀看圖解

線程與進程的區別可以歸納為以下4點:   1)地址空間和其它資源(如打開文件):進程間相互獨立,同一進程的各線程間共享。某進程內的線程在其它進程不可見。   2)通信:進程間通信IPC,線程間可以直接讀寫進程數據段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數據的一致性。   3)調度和切換:線程上下文切換比進程上下文切換要快得多。   4)在多線程操作系統中,進程不是一個可執行的實體。

一個進程中的多個線程能夠並行麽?

  不行 因為Cpython中有一把全局解釋器鎖,GIL 所以所線程不能充分利用多核,

  同一時刻用一個進程中的線程只有一個能被CPU執行

  GIL鎖 確實是限制了你的程序效率

  GIL鎖目前是能夠幫助你在線程中的切換中提高效率

內存中的線程

技術分享圖片

多個線程共享同一個進程的地址空間中的資源,是對一臺計算機上多個進程的模擬,有時也稱線程為輕量級的進程。

  而對一臺計算機上多個進程,則共享物理內存、磁盤、打印機等其他物理資源。多線程的運行也多進程的運行類似,是cpu在多個線程之間的快速切換。

  不同的進程之間是充滿敵意的,彼此是搶占、競爭cpu的關系,如果迅雷會和QQ搶資源。而同一個進程是由一個程序員的程序創建,所以同一進程內的線程是合作關系,一個線程可以訪問另外一個線程的內存地址,大家都是共享的,一個線程幹死了另外一個線程的內存,那純屬程序員腦子有問題。

  類似於進程,每個線程也有自己的堆棧,不同於進程,線程庫無法利用時鐘中斷強制線程讓出CPU,可以調用thread_yield運行線程自動放棄cpu,讓另外一個線程運行。

  線程通常是有益的,但是帶來了不小程序設計難度,線程的問題是:

  1. 父進程有多個線程,那麽開啟的子線程是否需要同樣多的線程

  2. 在同一個進程中,如果一個線程關閉了文件,而另外一個線程正準備往該文件內寫內容呢?

  因此,在多線程的代碼中,需要更多的心思來設計程序的邏輯、保護程序的數據。


threading模塊

創建線程

def func(i):
    print(子線程 :,i,os.getpid())

print(主線程 :,os.getpid())
for i in range(10):
    t = Thread(target=func,args=(i,))
    t.start()

Thread實例對象的方法

  • isAlive() :返回線程是否活動的
  • getName() : 返回線程名
  • setName() : 設置線程名
from threading import currentThread
def func():
    time.sleep(3)

t = Thread(target=func)
t.start()
print(t.is_alive())
print(t.getName())
t.setName(t1)
print(t.getName())

threading模塊提供的一些方法:(不常用)
  threading.currentThread(): 返回當前的線程變量。
m threading import currentThread
def func():
    print(子線程 : ,currentThread().name) #返回線程名
    time.sleep(3)

print(主線程 : ,currentThread().ident) #返回線程地址
t = Thread(target=func)
t.start()
threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程。
from threading import currentThread
from threading import enumerate
def func():
    print(子線程 : ,currentThread().ident)
    time.sleep(3)

print(主線程 : ,currentThread().ident)
for i in range(10):
    t = Thread(target=func)
    t.start()
print(len(enumerate()))

  threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。
from threading import currentThread
from threading import activeCount
def func():
    print(子線程 : ,currentThread().ident)
    time.sleep(3)

print(主線程 : ,currentThread().ident)
for i in range(10):
    t = Thread(target=func)
    t.start()
print(activeCount())

守護線程

無論是進程還是線程,都遵循:守護者會等待主程序運行完畢後被銷毀。需要強調的是:運行完畢並非終止運行

  • 對主進程來說,運行完畢指的是主進程代碼運行完畢
  • 對主線程來說,運行完畢指的是主線程所在的程序內所有的非守護線程全部運行完畢,主線程才算運行完畢
主進程在其代碼結束後就已經算運行完畢了(守護進程在此時就被回收),
然後主進程會一直等非守護的子進程都運行完畢後回收子進程的資源(否則會產生僵屍進程),才會結束
主線程在其他非守護線程運行完畢後才算運行完畢(守護線程在此時就被回收)。
因為主線程的結束意味著進程的結束,進程整體的資源都將被回收,
而進程必須保證非守護線程都運行完畢後才能結束。

方法一

time
from threading import Thread
def func1():
    while True:
        time.sleep(0.5)
        print(123)

def func2():
    print(func2 start)
    time.sleep(3)
    print(func2 end)

t1 = Thread(target=func1)
t2 = Thread(target=func2)
t1.setDaemon(True)  #添加守護線程
t1.start()
t2.start()
print(主線程的代碼結束)

方法二

from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")


t1=Thread(target=foo)
t2=Thread(target=bar)

t1.daemon=True
t1.start()
t2.start()
print("main-------")


線程 threading模塊