Python - 并发线程的实现
在本章中,我们将学习如何在 Python 中实现线程。
用于线程实现的 Python 模块
Python 线程有时被称为轻量级进程,因为线程占用的内存比进程少得多。线程允许一次执行多个任务。在 Python 中,我们有以下两个模块,它们在程序中实现线程 -
- <_thread>模块
- <threading>模块
这两个模块之间的主要区别在于<_thread>模块将线程视为函数,而 <threading> 模块将每个线程视为一个对象并以面向对象的方式实现它。此外,<_thread> 模块在低级线程中有效,并且功能少于 <threading> 模块。
<_thread>模块
在早期版本的 Python 中,我们有 <thread> 模块,但它被认为“已弃用”很长一段时间。鼓励用户改用 <threading> 模块。因此,在 Python 3 中,模块 “thread” 不再可用。它已重命名为 “<_thread>”,以解决 Python3 中的向后不兼容问题。
要在 <_thread> 模块的帮助下生成新线程,我们需要调用它的 start_new_thread 方法。这种方法的工作原理可以借助以下语法来理解 -
_thread.start_new_thread ( function, args[, kwargs] )
这里 -
- args :是参数的元组
- kwargs :是一个可选的关键字参数字典
如果我们想在不传递参数的情况下调用 function,那么我们需要在 args 中使用空的参数元组。
此方法调用立即返回,子线程启动,并使用传递的 args 列表(如果有)调用 function。线程在函数返回时终止。
例以下是使用 <_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> 模块的帮助下 b.
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() − 此方法返回当前处于活动状态的所有线程对象的列表。为了实现线程,<threading> 模块具有 Thread 类,该类提供以下方法 -
- run() − run() 方法是线程的入口点。
- start() − start() 方法通过调用 run 方法来启动线程。
- join([time]) − join() 等待线程终止。
- isAlive() − isAlive() 方法检查线程是否仍在执行。
- getName() − getName() 方法返回线程的名称。
- setName() − setName() 方法设置线程的名称。
如何使用 <threading> 模块创建线程?
在本节中,我们将学习如何使用 <threading> 模块创建线程。按照以下步骤使用 <threading> 模块创建新线程 -
- 步骤 1 - 在此步骤中,我们需要定义 Thread 类的新子类。
- 步骤 2 - 然后为了添加额外的参数,我们需要覆盖 __init__(self [,args]) 方法。
- 步骤 3 − 在此步骤中,我们需要重写 run(self [,args]) 方法来实现线程启动时应该做什么。
现在,在创建新的 Thread 子类之后,我们可以创建它的实例,然后通过调用 start() 来启动一个新线程,而 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: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 程序
有五个线程状态 - new、runnable、running、waiting 和 dead。在这五个中,我们将主要关注三种状态——running、waiting 和 dead。线程获取处于 running 状态的资源,等待处于 waiting 状态的资源;资源的最终版本(如果执行并获取)处于 Dead 状态。
以下 Python 程序在 start()、sleep() 和 join() 方法的帮助下,将分别显示线程如何进入正在运行、等待和死亡状态。
第 1 步 - 导入必要的模块,<threading> 和 <time>
import threading
import time
第 2 步 - 定义一个函数,该函数将在创建线程时调用。
def thread_states():
print("Thread entered in running state")
第 3 步 - 我们使用 time 模块的 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 中,我们可以通过不同的方式启动一个新线程,但其中最简单的方法是将其定义为单个函数。定义函数后,我们可以将其作为新线程的目标传递。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())
输出
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 中实现守护线程之前,我们需要了解守护线程及其用法。在计算方面,daemon 是一个后台进程,负责处理数据发送、文件传输等各种服务的请求。如果不再需要它,它将处于休眠状态。同样的任务也可以在非守护进程线程的帮助下完成。但是,在这种情况下,主线程必须手动跟踪非守护程序线程。另一方面,如果我们使用的是守护线程,那么主线程可以完全忘记这一点,当主线程退出时它将被杀死。关于守护线程的另一个重要点是,我们可以选择仅将它们用于非必要的任务,如果它没有完成或在两者之间被杀死,则不会影响我们。以下是 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。我们可以借助以下输出来理解 non-daemon 和 daemon threads 之间的区别 -
starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello