多線程(一)
阿新 • • 發佈:2019-05-08
有效 直接 維護 什麽是 isa 一個 bject 默認 行為
什麽是線程?
?線程是一個進程的實體,一個進程可以擁有多個線程,一個線程必須有一個父進程。線程是由表示程序運行狀態的寄存器(如程序計數器、棧指針)以及堆棧組成,它是比進程更小的單位。
?線程是程序中的一個執行流。一個執行流是由CPU運行程序代碼並操作程序的數據所形成的。因此,線程被認為是以CPU為主體的行為。
?線程不包含進程地址空間中的代碼和數據,線程是計算過程在某一時刻的狀態。所以,系統在產生一個線程或各個線程之間切換時,負擔要比進程小得多。
?線程是一個用戶級的實體,線程結構駐留在用戶空間中,能夠被普通的用戶級函數直接訪問。
?一個線程本身不是程序,它必須運行於一個程序(進程)之中。因此,線程可以定義為一個程序中的單個執行流。
?多線程是指一個程序中包含多個執行流,多線程是實現並發的一種有效手段。一個進程在其執行過程中,可以產生多個線程,形成多個執行流。每個執行流即每個線程也有它自身的產生、存在和消亡的過程。
?多線程程序設計的含義就是可以將程序任務分成幾個並行的子任務。
線程和進程的區別
?進程是資源分配的最小單位,線程是程序執行的最小單位
? 進程有自己的獨立地址空間,每啟動一個進程,系統就會為它分配地址空間,建立數據表來維護代碼段、堆棧段和數據段,這種操作非常昂貴。而線程是共享進程中的數據的,使
用相同的地址空間,因此CPU切換一個線程的花費遠比進程要小很多,同時創建一個線程
的開銷也比進程要小很多。
? 線程之間的通信更方便,同一進程下的線程共享全局變量、靜態變量等數據,而進程之間的通信需要以通信的方式(IPC)進行。不過如何處理好同步與互斥是編寫多線程程序的難
點。
? 但是多進程程序更健壯,多線程程序只要有一個線程死掉,整個進程也死掉了,而一個進程死掉並不會對另外一個進程造成影響,因為進程有自己獨立的地址空間。
線程的狀態圖
線程阻塞通常是指一個線程在執行過程中暫停,以等待某個條件的觸發;
Python線程threading模塊
Threading模塊提供的類:
Thread,Lock,Rlock,Condition,Semaphore,Event,Timer,local等。
threading模塊提供的常用方法:
? threading.currentThread(): 返回當前的線程變量。
? threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線
程啟動後、結束前,不包括啟動前和終止後的線程。
? threading.activeCount():返回正在運行的線程數量,與len(threading.enumerate())有相同的結果
。
threading模塊提供了更方便的API來操作線程。
Thread是threading模塊中最重要的類之一,可以使用它來創建線程。
該類創建線程有有兩種方式:
1.直接創建threading.Thread類的對象,初始化時將可調用對象作為參數傳入。
2.通過繼承Thread類,重寫它的run方法。
Thread類的構造方法:
__init__(self, group=None, target=None, name=None, args=(),kwargs=None, verbose=None)
參數說明:
?group:線程組,目前還沒有實現,庫引用中提示必須是None;
?target:要執行的方法;
?name:線程名;
?args/kwargs:要傳入方法的參數。
Thread類的方法
1)isAlive():返回線程是否在運行。正在運行指的是啟動後,終止前。
2)getName(name):獲取線程名。
3)setName(name):設置線程名。
4)isDaemon(bool):判斷線程是否隨主線程一起結束。
5)setDaemon(bool):設置是否為守護線程。初始值從創建該線程的線程繼承而來,默認為False,當沒有非守護線程仍在運行時,程序將終止。比如,主線程A中,創建了子線程B,並且在主線程A中調用了B.setDaemon(),意思是,把線程B設置為守護線程,這時候,要是主線程A執行結束了,就不管子線程B是否完成,一並和主線程A退出。這就是setDaemon方法的含義,這基本和join是相反的。此外,還有個要特別註意,必須在start() 方法調用之前調用此方法,如果不設置為守護線程,程序有可能會被無限掛起。
6)start():啟動線程。
7)join([timeout]):阻塞當前上下文環境的線程,直到調用此方法的線程終止或到達指定的等待時間timeout(可選參數)。即當前的線程要等調用join()這個方法的線程執行完,或者是達到規定的時間。比如,主線程A中,創建了子線程B,並且在主線程A中調用了B.join(),那麽,主線程A會在調用的地方等待,直到子線程B完成操作後,才可以接著往下執行,那麽在調用這個線程時可以使用被調用線程的join方法。
8)run():用於表示線程活動的方法,通常需要重寫,編寫代碼實現做需要的功能。
創建threading.Thread線程
#encoding=utf-8
from threading import Thread
import time
def run(a = None,b = None):
print(a,b)
time.sleep(1)
#創建一個子線程
t = Thread(target=run, args=("this is a","thread"))
#獲取線程的名字
print(t.getName())
#判斷線程是否還活著,在start後,在執行完畢前調用isAlive()才會返回true
print(t.isAlive())
t.start()
print(t.isAlive())
t.join()
print(t.isAlive())
通過繼承Thread類創建線程
使用Threading模塊創建線程,直接從threading.Thread繼承,然後重寫__init__方法和run方法
#encoding=utf-8
from threading import Thread
import time
class MyThread(Thread):
#需要調用父類的__init__方法
def __init__(self,a):
super(MyThread,self).__init__()
#Thread.__init__(self)
self.a = a
#需重寫父類的run方法
def run(self):
time.sleep(self.a)
print("sleep:",self.a)
t1 = MyThread(2)
t2 = MyThread(3)
t1.start()
t2.start()
t1.join()
t2.join()
註意:多線程執行本身是沒有先後順序的
run方法是在start()時候自動調用的;
繼承Thread類-新增run方法參數
註意:
繼承Thread類的新類MyThread構造函數中必須要調用父類的構造方法,這樣才能產生父類的構造函數中的參數,才能產生線程所需要的參數。新的類中如果需要別的參數,直接在其構造方法中添加即可。同時,新類中,在重寫父類的run方法時,它默認是不帶參數的,如果需要給他提供參數,需要在類的構造函數中指定,因為在線程執行過程中,run方法是線程自己去調用的,不用我們手動調用,所以沒法直接給傳遞參數,只能在構造方法中設定好參數,然後在run方法中再去取這些參數。
#encoding=utf-8
import threading
import time
class timer(threading.Thread):
def __init__(self,num,interval):
threading.Thread.__init__(self)
self.thread_num = num
self.interval = interval
self.thread_stop = False
def run(self):
while not self.thread_stop:
print("Thread Object(%d),Time:%s\n" %(self.thread_num,time.ctime()))
time.sleep(self.interval)
def stop(self):
self.thread_stop = True
def test():
thread1 = timer(1,1)
thread2 = timer(2,2)
thread1.start()
thread2.start()
time.sleep(10)
thread1.stop()
thread2.stop()
if __name__ == "__main__":
test()
線程退出
下面的程序實現了多線程執行後自動退出,無需設定結束的時間也沒有使用join函數,主線程會自己退出。
#encoding=utf-8
import threading
import time
#如果flag設定為1線程啟動後會直接退出
exitFlag = 0
class MyThread(threading.Thread):
def __init__(self,threadID,name,counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
print_time(self.name,5,self.counter)
print("Exiting " + self.name)
def print_time(threadName,delay,counter):
while counter:#利用counter變量控制線程是否結束
if exitFlag:
thread.exit()
time.sleep(delay)
print("%s:%s" %(threadName,time.ctime(time.time())))
counter -= 1
thread1 = MyThread(1,"線程1",1)
thread2 = MyThread(1,"線程2",2)
thread1.start()
thread2.start()
print("主進程結束!")
Join函數用法
#encoding=utf-8
import threading
import time
def context(tJoin):
print("in threadContext")
tJoin.start()
tJoin.join()
print("out threadContext.")
def join():
print("in threadJoin.")
time.sleep(1)
print("out threadJoin")
tJoin = threading.Thread(target=join)
tContext = threading.Thread(target=context,args=(tJoin,))
tContext.start()
主程序中tJoin = threading.Thread(target=join)這句代碼執行後,只是創建了一個線程對象
tJoin,但並未啟動線程。
tContext = threading.Thread(target=context, args=(tJoin,))
tContext.start()
上面這兩句執行後,創建了一個線程對象tContext並啟動該線程(打印in threadContext.)
,同時將tJoin線程對象作為參數傳給context函數,在context函數中,啟動了tJoin這個線程,同時該線程又調用了join()函數(tJoin.join()),那tContext線程將等待tJoin這線程執行完成後,才能繼續tContext線程後面的,所以先執行join()函數,打印輸出下面兩句:
in threadJoin.out threadJoin.tJoin線程執行結束後,繼續執行tContext線程,於是打印輸出了out threadContext.,於是就看到我們上面看到的輸出結果,並且無論執行多少次,結果都是這個順序。但如果將context()函數中tJoin.join()這句註釋掉,再執行該程序,打印輸出的結果順序就不定了,因為此時這兩線程就是並發執行的;
守護線程
如果一個線程被設置為守護線程,不會阻礙主線程的退出,會隨著主線程的退出而退出;
#encoding=utf-8
import threading
import time
class MyThread(threading.Thread):
def __init__(self,id):
threading.Thread.__init__(self)
def run(self):
time.sleep(5)
print("This is " + self.getName())
if __name__ == "__main__":
t1 = MyThread(999)
t1.setDaemon(True)#設置線程為守護線程
t1.start()
print("I am the father thread.")
由於線程啟動後,然後主線程就結束了,而子線程的run方法需要等待5秒,這時候子線程已經隨著主線程的退出而退出了;所以不會打印run裏面的語句;
從結果可以看出,調用了setDaemon()函數後,子線程被設置為守護線程,主線程打印內容後就結束了,不管子線程是否執行完畢,一並被終止了。可見join()函數與setDaemon()函數功能是相反的。
多線程(一)