Concurrency In Python 简明教程
Implementation of Threads
在本章中,我们将学习如何在 Python 中实现线程。
Python Module for Thread Implementation
Python 线程有时被称为轻量级进程,因为线程占用的内存远少于进程。线程允许一次执行多个任务。在 Python 中,我们有以下两个模块在程序中实现线程——
-
*<_thread>*module
-
*<threading>*module
这两个模块的主要差别在于 <_thread> 模块将线程作为函数处理,而 <threading> 模块将每个线程都作为对象处理,并以面向对象的方式实现它。此外, <_thread>*module is effective in low level threading and has fewer capabilities than the *<threading> 模块。
<_thread> module
在早期版本的 Python 中,我们曾使用过 <thread> 模块,但它很长时间都被视为“已弃用”。我们鼓励用户使用 <threading> 模块。因此,在 Python 3 中不再提供 “thread” 模块。为了与 Python3 中向后不兼容,它已被重命名为 <_thread> 。
要借助 <_thread> 模块生成新线程,我们需要调用它的 start_new_thread 方法。此方法的工作原理可通过以下语法得到理解 −
_thread.start_new_thread ( function, args[, kwargs] )
这里 -
-
args 是一个元组参数
-
kwargs 是关键字参数(可选)的字典
如果我们想要在不传递参数的情况下调用函数,那么需要在 args 中使用一个空元组参数。
此方法调用会立即返回,子线程会启动,并使用已传递的参数列表(如果有的话)调用函数。当函数返回时,该线程会终止。
Example
以下是使用 <_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
Output
以下的输出将帮助我们了解如何借助 <_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
Additional methods in the <threading> module
<threading> 模块包含 <_thread> 模块的所有方法,但也提供了附加方法。附加方法如下 −
-
threading.activeCount() − 此方法返回活动线程对象的数目
-
threading.currentThread() − 此方法返回调用线程控制中的线程对象数目。
-
threading.enumerate() − 此方法返回当前处于活动状态的所有线程对象的列表。
How to create threads using the <threading> module?
在本节中,我们将学习如何使用 <threading> 模块创建线程。请按照以下步骤使用 <threading> 模块创建新线程 −
-
Step 1 − 在此步骤中,我们需要定义 Thread 类的子类。
-
Step 2 − 然后,为了添加附加参数,我们需要覆盖 init(self [,args]) 方法。
-
Step 3 − 在此步骤中,我们需要覆盖 run(self [,args]) 方法,以实现线程启动后应当执行的操作。
Example
请考虑此示例,以了解如何使用 <threading> 模块生成新线程。
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
Output
现在,请考虑以下输出:
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
Python Program for Various Thread States
有五种线程状态 - 新建、可运行、运行、等待和终止。在这五种中,我们主要关注三种状态 - 运行、等待和终止。一个线程在其运行状态获得其资源,在其等待状态等待资源;如果执行且已获取,资源在终止状态的最终释放。
以下 Python 程序借助 start()、sleep() 和 join() 方法将分别显示一个线程如何进入运行、等待和终止状态。
Step 1 - 导入必要的模块,<threading> 和 <time>
import threading
import time
Step 2 - 定义一个将在创建线程时调用的函数。
def thread_states():
print("Thread entered in running state")
Step 3 - 我们正在使用 time 模块的 sleep() 方法,让我们的线程等待 2 秒左右。
time.sleep(2)
Step 4 - 现在,我们创建了一个名为 T1 的线程,它采用上述定义函数的参数。
T1 = threading.Thread(target=thread_states)
Step 5 - 现在,借助 start() 函数,我们可以启动我们的线程。它将生成消息,该消息已在我们定义函数时设定。
T1.start()
Thread entered in running state
Step 6 - 现在,在最后,我们可以在线程完成其执行后使用 join() 方法终止它。
T1.join()
Starting a thread in Python
在 Python 中,我们可以通过不同的方式启动一个新线程,但其中最简单的一种方法是将其定义为一个单独的函数。在定义函数后,我们可以将其作为新 threading.Thread 对象的目标来传递,以此类推。执行以下 Python 代码,以了解该函数的工作原理:
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())
Output
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
Daemon threads in Python
在 Python 中实现守护线程之前,我们需要了解守护线程及其用法。在计算术语中,守护程序是一个后台进程,它处理各种服务(如数据发送、文件传输等)的请求。如果它不再需要,它将保持不活动。也可以借助非守护线程来完成相同任务。然而,在这种情况下,主线程必须手动跟踪非守护线程。另一方面,如果我们使用守护线程,那么主线程可以完全忘记这一点,当主线程退出时它将被终止。关于守护线程的另一个重点是,我们只能选择对非重要任务使用它们,这些任务在未完成或中途被终止时不会影响我们。以下是在 Python 中实现守护线程:
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()
在上述代码中,有两个函数,分别是 >nondaemonThread() 和 >daemonThread() 。第一个函数打印其状态并睡 8 秒,而 deamonThread() 函数则无限期地每 2 秒打印一次 Hello。借助以下输出,我们可以了解非守护线程和守护线程之间的区别:
Hello
starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello