📜  在Python中启动和停止线程

📅  最后修改于: 2022-05-13 01:54:58.853000             🧑  作者: Mango

在Python中启动和停止线程

线程库可用于在其自己的线程中执行任何可调用的Python 。为此,请创建一个 Thread 实例并提供您希望作为目标执行的可调用对象,如下面的代码所示 -

代码#1:
# Code to execute in an independent thread
import time
  
def countdown(n):
    while n > 0:
        print('T-minus', n)
        n -= 1
        time.sleep(5)
          
# Create and launch a thread
from threading import Thread
t = Thread(target = countdown, args =(10, ))
t.start() 

创建线程实例时,它不会开始执行,直到它的start()方法(使用您提供的参数调用目标函数)被调用。线程在它们自己的系统级线程(例如,POSIX 线程或 Windows 线程)中执行,完全由主机操作系统管理。一旦启动,线程就会独立运行,直到目标函数返回。代码 #2:查询线程实例以查看它是否仍在运行。

if t.is_alive():
    print('Still running')
else:
    print('Completed')

也可以请求加入一个等待它终止的线程。

t.join()

解释器保持运行直到所有线程终止。对于长时间运行的线程或永远运行的后台任务,请考虑使线程成为守护进程。代码#3:

t = Thread(target = countdown, args =(10, ), daemon = True)
t.start()

无法加入守护线程。但是,当主线程终止时,它们会自动销毁。除了显示的两个操作之外,与线程没有太多其他关系。例如,不存在终止线程、向线程发出信号、调整其调度或执行任何其他高级操作的操作。要拥有这些功能,请自行构建它们。为了能够终止线程,线程必须被编程为轮询
在选定的点退出。例如,将您的线程放在下面代码中提到的类中 -

代码#4:将线程放在一个类中。

class CountdownTask:
      
    def __init__(self):
    self._running = True
      
def terminate(self):
    self._running = False
      
def run(self, n):
    while self._running and n > 0:
        print('T-minus', n)
        n -= 1
        time.sleep(5)
  
c = CountdownTask()
t = Thread(target = c.run, args =(10, ))
t.start()
...
# Signal termination
c.terminate() 
  
# Wait for actual termination (if needed) 
t.join() 

如果线程执行阻塞操作(例如 I/O),则轮询线程终止可能很难协调。例如,在 I/O 操作上无限期阻塞的线程可能永远不会返回检查它是否被杀死。为了正确处理这种情况,线程需要仔细编程以利用超时循环,如下面的代码所示。

代码#5:

class IOTask:
    def terminate(self):
        self._running = False
          
        def run(self, sock):
            # sock is a socket
              
            # Set timeout period
            sock.settimeout(5) 
            while self._running:
                  
                # Perform a blocking I/O operation w/timeout
                try:
                    data = sock.recv(8192)
                    break
                except socket.timeout:
                    continue
                # Continued processing
                ...
        # Terminated
        return

由于全局解释器锁 (GIL), Python线程被限制为只允许一个线程在任何给定时间在解释器中执行的执行模型。出于这个原因, Python线程通常不应该用于试图在多个 CPU 上实现并行性的计算密集型任务。它们更适合于 I/O 处理和处理执行阻塞操作的代码中的并发执行(例如,等待 I/O、等待来自数据库的结果等)。代码 #6:通过从 Thread 类继承定义的线程

from threading import Thread
  
class CountdownThread(Thread):
    def __init__(self, n):
        super().__init__()
        self.n = 0
          
    def run(self):
        while self.n > 0:       
    print('T-minus', self.n)
    self.n -= 1
    time.sleep(5)
      
c = CountdownThread(5)
c.start()

尽管这可行,但它在代码和线程库之间引入了额外的依赖关系。也就是说,只有生成的代码可以在线程的上下文中使用,而前面显示的技术涉及编写不显式依赖线程的代码。通过释放此类依赖项的代码,它可以在可能涉及或不涉及线程的其他上下文中使用。例如,可以使用下面给出的代码使用多处理模块在单独的进程中执行代码 -

代码#7:

import multiprocessing
c = CountdownTask(5)
p = multiprocessing.Process(target = c.run)
p.start()
...

同样,这仅在CountdownTask class以与实际并发方式(线程、进程等)无关的方式编写时才有效。