线程的实现



本章我们将学习如何在 Python 中实现线程。

Python 线程实现模块

Python 线程有时被称为轻量级进程,因为线程占用的内存远小于进程。线程允许同时执行多个任务。在 Python 中,我们有两个实现程序中线程的模块:

  • <_thread> 模块

  • <threading> 模块

这两个模块的主要区别在于,<_thread> 模块将线程视为函数,而 <threading> 模块将每个线程视为对象,并以面向对象的方式实现它。此外,<_thread> 模块在低级线程中有效,并且功能比 <threading> 模块少。

<_thread> 模块

在早期版本的 Python 中,我们有 <thread> 模块,但它已被认为是“已弃用”很长时间了。鼓励用户改用 <threading> 模块。因此,在 Python 3 中,"thread" 模块不再可用。为了向后兼容 Python 3,它已被重命名为 "<_thread>"。

要借助 <_thread> 模块生成新线程,我们需要调用它的 start_new_thread 方法。此方法的工作原理可以通过以下语法来理解:

_thread.start_new_thread ( function, args[, kwargs] )

这里:

  • args 是参数的元组

  • kwargs 是可选的关键字参数字典

如果我们想在不传递参数的情况下调用函数,则需要在 args 中使用空的元组参数。

此方法调用立即返回,子线程启动并使用传递的 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() - 此方法返回当前活动的所有线程对象的列表。

  • 为了实现线程,<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() 启动新线程,这反过来又会调用 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 - 我们使用 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 中,我们可以通过不同的方式启动新线程,但其中最简单的一种是将其定义为单个函数。定义函数后,我们可以将其作为新 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())

输出

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
广告