在本章中,我們將學(xué)習(xí)如何在Python中實(shí)現(xiàn)線程。
Python線程有時(shí)稱為輕量級(jí)進(jìn)程,因?yàn)榫€程比進(jìn)程占用的內(nèi)存少得多。 線程允許一次執(zhí)行多個(gè)任務(wù)。 在Python中,以下兩個(gè)模塊在一個(gè)程序中實(shí)現(xiàn)線程 -
_thread 模塊threading 模塊這兩個(gè)模塊之間的主要區(qū)別在于_thread模塊將線程視為一個(gè)函數(shù),而threading模塊將每個(gè)線程視為一個(gè)對(duì)象并以面向?qū)ο蟮姆绞綄?shí)現(xiàn)它。 此外,_thread模塊在低級(jí)線程中有效并且比threading模塊具有更少的功能。
在Python的早期版本中,擁有thread模塊,但在相當(dāng)長的一段時(shí)間里它已被視為“已棄用”。 鼓勵(lì)用戶改用threading模塊。 因此,在Python 3中,thread模塊不再可用。 它已被重命名為_thread,用于Python3中的向后不兼容。
為了在_thread模塊的幫助下生成新的線程,我們需要調(diào)用它的start_new_thread方法。 這種方法的工作可以通過以下語法來理解 -
_thread.start_new_thread ( function, args[, kwargs] )
這里,
args是一個(gè)參數(shù)的元組kwargs是關(guān)鍵字參數(shù)的可選字典如果想在不傳遞參數(shù)的情況下調(diào)用函數(shù),那么需要在args中使用一個(gè)空的參數(shù)元組。
此方法調(diào)用立即返回,子線程啟動(dòng),并調(diào)用與傳遞的列表(如果有的話)args的函數(shù)。 線程在函數(shù)返回時(shí)終止。
示例
以下是使用_thread模塊生成新線程的示例。在這里使用start_new_thread()方法。
import _thread
import time
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
try:
_thread.start_new_thread( print_time, ("Thread-1", 2, ) )
_thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print ("Error: unable to start thread")
while 1:
pass
在_thread模塊的幫助下理解新線程的生成。
Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018
threading模塊以面向?qū)ο蟮姆绞綄?shí)現(xiàn),并將每個(gè)線程視為一個(gè)對(duì)象。 因此,它為線程提供了比_thread模塊更強(qiáng)大,更高層次的支持。該模塊包含在Python 2.4中。
threading模塊包含_thread模塊的所有方法,但它也提供了其他方法。 其他方法如下 -
threading.activeCount() - 此方法返回處于活動(dòng)狀態(tài)的線程對(duì)象的數(shù)量threading.currentThread() - 此方法返回調(diào)用者線程控制中的線程對(duì)象數(shù)。threading.enumerate() - 此方法返回當(dāng)前活動(dòng)的所有線程對(duì)象的列表。為了實(shí)現(xiàn)線程,threading模塊具有提供以下方法的Thread類 -
run() - run()方法是線程的入口點(diǎn)。start() - start()方法通過調(diào)用run方法來啟動(dòng)線程。join([time]) - join()等待線程終止。isAlive() - isAlive()方法檢查線程是否仍在執(zhí)行。getName() - getName()方法返回線程的名稱。setName() - setName()方法設(shè)置線程的名稱。在本節(jié)中,我們將學(xué)習(xí)如何使用threading模塊創(chuàng)建線程。 按照以下步驟使用threading模塊創(chuàng)建一個(gè)新線程 -
第1步 - 在這一步中,需要定義Thread類的新子類。
第2步 - 然后為了添加額外的參數(shù),需要重寫__init __(self [,args])方法。
第3步 - 在這一步中,需要重寫run(self [,args])方法來實(shí)現(xiàn)線程在啟動(dòng)時(shí)應(yīng)該執(zhí)行的操作。
現(xiàn)在,在創(chuàng)建新的Thread子類后,可以創(chuàng)建它的一個(gè)實(shí)例,然后通過調(diào)用start()來啟動(dòng)一個(gè)新線程,start()又調(diào)用run()方法。
示例
下面這個(gè)例子演示如何使用threading模塊生成一個(gè)新的線程。
import threading
import time
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, self.counter, 5)
print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Starting Thread-1
Starting Thread-2
執(zhí)行上面示例代碼,得到以下結(jié)果 -
Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread
有五種線程狀態(tài) - 新的,可運(yùn)行,運(yùn)行,等待和死亡。 在這五個(gè)中,我們將主要關(guān)注三個(gè)狀態(tài) - 運(yùn)行,等待和死亡。 一個(gè)線程獲取處于運(yùn)行狀態(tài)的資源,等待處于等待狀態(tài)的資源; 如果執(zhí)行和獲取的資源的最終版本處于死亡狀態(tài)。
下面的Python程序在start(),sleep()和join()方法的幫助下將分別顯示線程是如何進(jìn)入運(yùn)行,等待和死亡狀態(tài)的。
第1步 - 導(dǎo)入必要的模塊,threading和time
import threading
import time
第2步 - 定義一個(gè)函數(shù),它將在創(chuàng)建線程時(shí)調(diào)用。
def thread_states():
print("Thread entered in running state")
第3步 - 使用time模塊的sleep()方法讓線程等待2秒鐘。
time.sleep(2)
第4步 - 現(xiàn)在,創(chuàng)建一個(gè)名為T1的線程,它接受上面定義的函數(shù)的參數(shù)。
T1 = threading.Thread(target=thread_states)
第5步 - 現(xiàn)在,使用start()函數(shù),可以開始啟動(dòng)線程。 它會(huì)產(chǎn)生這個(gè)信息,這個(gè)信息是在定義函數(shù)時(shí)設(shè)定的。
T1.start()
# Thread entered in running state
第6步 - 現(xiàn)在,最后可以在完成執(zhí)行后使用join()方法終止線程。
T1.join()
在python中,可以通過不同的方式啟動(dòng)一個(gè)新的線程,但其中最簡單的一個(gè)就是將其定義為一個(gè)單一的函數(shù)。 在定義函數(shù)之后,可以將它作為新線程的目標(biāo)。線程對(duì)象等等。 執(zhí)行下面的Python代碼來理解函數(shù)的工作原理 -
import threading
import time
import random
def Thread_execution(i):
print("Execution of Thread {} started\n".format(i))
sleepTime = random.randint(1,4)
time.sleep(sleepTime)
print("Execution of Thread {} finished".format(i))
for i in range(4):
thread = threading.Thread(target=Thread_execution, args=(i,))
thread.start()
print("Active Threads:" , threading.enumerate())
執(zhí)行上面代碼,得到以下結(jié)果 -
Execution of Thread 0 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>]
Execution of Thread 1 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>]
Execution of Thread 2 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>]
Execution of Thread 3 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>,
<Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished
在python中,可以通過不同的方式啟動(dòng)一個(gè)新的線程,但最簡單的就是將其定義為一個(gè)單一的函數(shù)。 在定義函數(shù)之后,可以將它作為新線程的目標(biāo)。線程對(duì)象等等。 執(zhí)行下面的Python代碼來理解函數(shù)的工作原理 -
import threading
import time
def nondaemonThread():
print("starting my thread")
time.sleep(8)
print("ending my thread")
def daemonThread():
while True:
print("Hello")
time.sleep(2)
if __name__ == '__main__':
nondaemonThread = threading.Thread(target = nondaemonThread)
daemonThread = threading.Thread(target = daemonThread)
daemonThread.setDaemon(True)
daemonThread.start()
nondaemonThread.start()
在上面的代碼中,有兩個(gè)函數(shù),分別是- nondaemonThread()和daemonThread()。 第一個(gè)函數(shù)打印其狀態(tài)并在8秒后休眠,而deamonThread()函數(shù)每2秒無限期地打印出Hello。 我們可以通過以下輸出來了解nondaemon和daemon線程之間的區(qū)別 -
Hello
starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello