线程的实现
在本章中,我们将学习如何在 Python 中实现线程。
用于线程实现的 Python 模块
Python 线程有时被称为轻量级进程,因为线程占用的内存比进程少得多。线程允许一次执行多个任务。在 Python 中,我们有以下两个模块在程序中实现线程 −
<_thread>模块
<threading>模块
这两个模块之间的主要区别在于,<_thread> 模块将线程视为函数,而 <threading> 模块将每个线程视为对象并以面向对象的方式实现。此外,<_thread> 模块在低级线程中有效,并且功能少于 <threading> 模块。
<_thread> 模块
在早期版本的 Python 中,我们有 <thread> 模块,但它已被视为"弃用"很长时间。鼓励用户改用 <threading> 模块。因此,在 Python 3 中,"thread"模块不再可用。为了与 Python3 兼容,它已被重命名为"<_thread>"。
要借助 <_thread> 模块生成新线程,我们需要调用它的 start_new_thread 方法。借助以下语法 − 可以理解此方法的工作原理。
_thread.start_new_thread ( function, args[, kwargs] )
此处 −
args 是参数的元组
kwargs 是关键字参数的可选字典
如果我们想在不传递参数的情况下调用函数,则需要在 args 中使用一个空的参数元组。
此方法调用立即返回,子线程启动,并使用传递的参数列表(如果有)调用函数。当函数返回时,线程终止。
示例
以下是使用 <_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> 模块
<threading> 模块以面向对象的方式实现,并将每个线程视为一个对象。因此,它比 <_thread> 模块提供了更强大、更高级的线程支持。此模块包含在 Python 2.4 中。
<threading> 模块中的其他方法
<threading> 模块包含 <_thread> 模块的所有方法,但它还提供其他方法。其他方法如下 −
threading.activeCount() − 此方法返回处于活动状态的线程对象的数量
threading.currentThread() −此方法返回调用者线程控制中的线程对象数量。
threading.enumerate() − 此方法返回当前处于活动状态的所有线程对象的列表。
run() − run() 方法是线程的入口点。
start() − start() 方法通过调用 run 方法来启动一个线程。
join([time]) − join() 等待线程终止。
isAlive() − isAlive() 方法检查线程是否仍在执行。
getName() − getName() 方法返回线程的名称。
setName() − setName() 方法设置线程的名称。
为了实现线程,<threading> 模块具有 Thread 类,该类提供以下方法 −
如何使用 <threading> 创建线程模块?
在本节中,我们将学习如何使用 <threading> 模块创建线程。按照以下步骤使用 <threading> 模块创建新线程 −
步骤 1 − 在此步骤中,我们需要定义 Thread 类的新子类。
步骤 2 − 然后,为了添加其他参数,我们需要重写 __init__(self [,args]) 方法。
步骤 3 −在此步骤中,我们需要重写 run(self [,args]) 方法来实现线程启动时应该执行的操作。
现在,在创建新的 Thread 子类后,我们可以创建它的一个实例,然后通过调用 start() 来启动一个新线程,而后者又调用 run() 方法。
示例
查看此示例以了解如何使用 <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
输出
现在,考虑以下输出 −
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 程序
线程有五种状态 - 新建、可运行、正在运行、等待和死亡。在这五种状态中,我们将主要关注三种状态 - 运行、等待和死亡。线程在运行状态下获取资源,在等待状态下等待资源;如果正在执行并获取资源,则最终释放资源,处于死亡状态。
以下 Python 程序借助 start()、sleep() 和 join() 方法分别显示线程如何进入运行、等待和死亡状态。
步骤 1 − 导入必要的模块,<threading> 和 <time>
import threading import time
步骤 2 − 定义一个函数,该函数将在创建线程时调用。
def thread_states(): print("Thread entered in running state")
步骤 3 − 我们使用时间模块的 sleep() 方法使我们的线程等待 2 秒。
time.sleep(2)
步骤 4 −现在,我们创建一个名为 T1 的线程,它接受上面定义的函数的参数。
T1 = threading.Thread(target=thread_states)
步骤 5 − 现在,借助 start() 函数,我们可以启动线程。它将生成我们在定义函数时设置的消息。
T1.start() Thread entered in running state
步骤 6 − 现在,我们终于可以在线程执行完毕后,使用 join() 方法终止该线程了。
T1.join()
在 Python 中启动线程
在 Python 中,我们可以通过不同的方式启动新线程,但其中最简单的方法是将其定义为单个函数。定义函数后,我们可以将其作为新 threading.Thread 对象的目标传递,依此类推。执行以下 Python 代码以了解该函数的工作原理 −
import threading import time import random def Thread_execution(i): print("Execution of Thread {} started ".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())
输出
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 中的守护线程
在 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