Programación de subprocesos múltiples de Python: cómo cerrar correctamente los subprocesos

En la programación concurrente, podemos crear un nuevo subproceso, ejecutar tareas en él y decidir detener el subproceso por algún motivo. Por ejemplo:

  • El resultado de la tarea del subproceso ya no es necesario.
  • La aplicación se está cerrando.
  • Puede haber ocurrido una excepción en la ejecución del hilo

Para conocer la programación multiproceso de Python, consulte Programación multiproceso de Master Python de superficial a profunda

La clase Thread del módulo Threading no proporciona un método para cerrar un hilo. Si el subproceso secundario no se cierra correctamente, se pueden encontrar los siguientes problemas:

  • Después de que finaliza el subproceso principal, el subproceso secundario sigue ejecutándose y se convierte en un proceso zombi
  • El archivo abierto por el subproceso secundario no se cerró correctamente, lo que provocó la pérdida de datos
  • La base de datos abierta por el subproceso secundario no pudo enviar la actualización, lo que provocó la pérdida de datos

Entonces, ¿cómo cerrar el hilo correctamente?

1. Cómo cierra Python los hilos por defecto

Después de crear el objeto de subproceso, llame al método start (para ejecutarlo y, una vez completada la ejecución, se cerrará automáticamente. Como en el siguiente código de ejemplo:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading    #导入threading 模块
import time

# 定义任务函数 print_time
def print_time ( threadName ,delay ):
    count = 0
    while count < 5:
        time.sleep(delay)
        count += 1
        print("%s: %s \n" % (threadName,time.ctime(time.time())))

# 定义任务函数 print_cube
def print_cube(num):
    #pring cube
    print("Cube:{} \n".format(num*num*num))
 
# 创建两个线程
if __name__ == "__main__":
        # 创建两个子线程
        t1 = threading.Thread( target=print_cube,args=(10,))
        t2 = threading.Thread( target=print_time,args=("Thread-2",4,))
        #start threads
        t1.start()   # start 后,子线程开始运行
        t2.start()
        t1.join()     #join 命令:让主线程暂停运行,等待子线程运行结束。
        t2.join()
        print("Done") # The statement is executed after sub threads done

2. ¿Cómo cerrar el hilo con gracia?

En el ejemplo de la sección anterior, el tiempo de ejecución del hilo es corto y puede terminar pronto, por lo que el hilo principal puede esperar a que termine. Sin embargo, si el hilo secundario ejecuta una tarea que consume mucho tiempo, como proporcionar un servicio o ejecutar una tarea de supervisión, puede haber un bucle permanente en el hilo secundario. En este momento, después de que el objeto del hilo secundario ejecuta start(), continuará procesando el estado de ejecución.

En el sistema WIndows, si la aplicación sale directamente, el subproceso se cerrará naturalmente a la fuerza, pero las tareas que ejecuta el subproceso pueden verse afectadas, por ejemplo, el archivo al que se accede puede cerrarse correctamente, lo que resulta en la pérdida de datos. , etc.
En el sistema Linux, si la aplicación se cierra directamente, como cuando se usa el comando kill para terminar el proceso, los subprocesos que no se cierran correctamente pueden seguir ejecutándose y convertirse en procesos zombis.

Entonces, ¿cómo detener con gracia el subproceso secundario? Hay dos ideas:
1) Cerrar el hilo configurando la variable de estado global
2) Cerrar el hilo a través del objeto threading.Event

El siguiente ejemplo muestra el proceso de implementación de los dos métodos.

2.1 Usar variables globales para cerrar hilos

Pasos de implementación:

  • Agregar variables de estado dentro del hilo
  • En el cuerpo del ciclo del subproceso, verifique la variable de estado, si es Falso, salga del ciclo.
  • Cuando el subproceso principal necesite cerrar el subproceso, establezca la variable de estado del objeto del subproceso secundario en False.

2.1.1 Cerrar el hilo implementado por la clase hilo

class CountdownTask:
      
    def __init__(self):
          self._running = True   # 定义线程状态变量
      
	def terminate(self):
	    self._running = False 
	      
	def run(self, n):
	    # run方法的主循环条件加入对状态变量的判断
	    while self._running and n > 0:
	        print('T-minus', n)
	        n -= 1
	        time.sleep(5)
	    print("thread is ended") 
  
c = CountdownTask()
th = Thread(target = c.run, args =(10, ))
th.start()
# 对于耗时线程,没必要再用join()方法了,注意主线程通常也需要有个监控循环
# … any code … 
# Signal termination
q = input("please press any key to quit ")
c.terminate() 

2.1.2 Cierre de hilos funcionales

Cerrar subprocesos funcionales, puede usar variables globales como variables de estado

import threading
import time
 
def run():
    while True:
        print('thread running')
        global stop_threads
        if stop_threads:
            break
 
stop_threads = False
t1 = threading.Thread(target = run)
t1.start()
time.sleep(1)
stop_threads = True
t1.join()
print('thread killed')


2.2 Usar el objeto threading.Event para cerrar el subproceso secundario

2.2.1 Principio de funcionamiento del mecanismo de eventos

Los eventos son una forma de comunicarse entre hilos. Su función es equivalente a una bandera global, y el subproceso principal coordina el ritmo de los subprocesos controlando el estado del objeto de evento.

Cómo utilizar

  1. El subproceso principal crea un objeto de evento y lo pasa como un parámetro al subproceso secundario
  2. El subproceso principal puede usar set()el método para eventestablecer el objeto en verdadero y clear()el método para establecerlo en falso.
  3. En el cuerpo del ciclo del subproceso secundario, verifique el valor del objeto de evento y, si es verdadero, salga del ciclo.
  4. Subproceso secundario, puede usar event.wait()para bloquear el proceso secundario actual hasta que el objeto de evento se establezca en verdadero.

Métodos comunes de la clase de evento

  • set() establece Verdadero
  • clear() establece Falso,
  • wait() hace que el proceso espere hasta que el indicador cambie a verdadero.
  • is_set() Consulta el objeto de evento, si se establece en verdadero, devuelve verdadero, de lo contrario, devuelve falso.
if event.is_set():
     # do something before end worker 
     break

La ventaja de este método es que el objeto Event es seguro para subprocesos y más rápido. Se recomienda utilizar este método para cerrar subprocesos que consumen mucho tiempo.

2.2.2 Código completo:

from time import sleep
from threading import Thread
from threading import Event
 
# define task function
def task(event):
    # execute a task in a loop
    for i in range(100):
        # block for a moment
        sleep(1)
        # check for stop
        if event.is_set():
            # 在此添加退出前要做的工作,如保存文件等
            break
        # report a message
        print('Worker thread running...')
    print('Worker is ended')
 
# create the event
event = Event()
# create a thread 
thread = Thread(target=task, args=(event,))
# start the new thread
thread.start()
# block for a while
sleep(3)
# stop the worker thread
print('Main stopping thread')
event.set()
# 这里是为了演示,实际开发时,主进程有事件循环,耗时函数不需要调用join()方法
thread.join()

El subproceso secundario ejecuta su bucle de tareas y comprueba el objeto de evento cada vez que se repite. Si el objeto sigue siendo falso, no activará la detención del subproceso.

Después de que el subproceso principal llama al método set() del objeto de evento, en el cuerpo del bucle del subproceso, llame al método is_set() del objeto de evento, y cuando se determina que el objeto de evento es Verdadero, sale inmediatamente el bucle de tareas y finaliza la operación.

Supongo que te gusta

Origin blog.csdn.net/captain5339/article/details/128360804
Recomendado
Clasificación