¿guardia? ¿Bloqueo de intérprete global? Conocimiento de subprocesos de proceso inminente de Python-Habilidades de desarrollo IX

        El artículo principal no es muy práctico en el desarrollo web. Actualmente, las plataformas en la nube prevalecen y dependen de las capacidades de la plataforma, el enfoque de CI/CD para abrir múltiples procesos de trabajo de los trabajadores para completar el trabajo, a excepción de algunos complementos de registro de monitoreo que usan subprocesos múltiples y procesos múltiples, el desarrollo web es relativamente raro. usado.

        Permítanme hablar primero sobre un desarrollo de Python que he escuchado: GIL, el bloqueo de intérprete global.

Tabla de contenido

Bloqueo de intérprete global GIL

Corrutina multiproceso multiproceso de Python

Subproceso del demonio del demonio de Python

modelo de pato

¿Por qué se inician los dos subprocesos cuando se ejecuta el servidor de ejecución?


Bloqueo de intérprete global GIL

        GIL (Global Interpreter Lock, Global Interpreter Lock) es un mecanismo de sincronización en el intérprete de CPython , que se utiliza para limitar varios subprocesos a un solo subproceso que ejecuta el código de bytes de Python al mismo tiempo . El GIL existe para simplificar la gestión de la memoria y resolver carreras de datos en subprocesos múltiples .

        Sin embargo, el GIL también hace que los subprocesos múltiples en CPython no aprovechen al máximo las CPU de varios núcleos y, por lo tanto, tengan un desempeño deficiente en las tareas de uso intensivo de la CPU. Para tareas intensivas de E/S, se pueden usar subprocesos múltiples, corrutinas o E/S asincrónicas para lograr la concurrencia. Para las tareas que hacen un uso intensivo de la CPU, considere el uso de multiprocesamiento para aprovechar al máximo las CPU de varios núcleos.

        Experimento: El siguiente es el uso de python 3.6. En el caso de hilo único y hilo múltiple, el tiempo de ejecución es muy similar. (Hay muchos resultados de ejecución en Internet que no son tan buenos como el tiempo de ejecución de un solo hilo)
        Nota: GIL solo se aplica al intérprete CPython. Por ejemplo, Jython y IronPython no implementan GIL, por lo que en estas implementaciones, el rendimiento de subprocesos múltiples puede ser mejor.

import threading
import time

def count(n):
    while n > 0:
        n -= 1

# 单线程执行
start_time = time.time()
count(100000000)
end_time = time.time()
print("单线程执行时间:", end_time - start_time)  # 单线程执行时间: 4.452801704406738

# 多线程执行
start_time = time.time()
t1 = threading.Thread(target=count, args=(50000000,))
t2 = threading.Thread(target=count, args=(50000000,))
t1.start()
t2.start()
t1.join()
t2.join()
end_time = time.time()
print("多线程执行时间:", end_time - start_time)  # 多线程执行时间: 4.272630214691162

Corrutina multiproceso multiproceso de Python

  1. Subprocesamiento múltiple: hay  threading módulos en la biblioteca estándar de Python que brindan compatibilidad con subprocesos múltiples (que se utilizó en los experimentos anteriores). Debido a Global Interpreter Lock (GIL), los subprocesos múltiples en CPython no pueden aprovechar al máximo las CPU multinúcleo y, por lo tanto, tienen un desempeño deficiente en las tareas que requieren un uso intensivo de la CPU. Pero en tareas intensivas de E/S, los subprocesos múltiples pueden mejorar la eficiencia de ejecución del programa.

  2. Multiprocesamiento:  multiprocessing los módulos de Python brindan soporte de multiprocesamiento. Multiproceso puede aprovechar al máximo la CPU multinúcleo, adecuada para tareas intensivas de CPU. Sin embargo, la comunicación entre procesos y el intercambio de recursos son relativamente complejos y costosos.

  3. Coroutine: Coroutine es una estrategia de concurrencia liviana que permite que varias tareas se ejecuten simultáneamente dentro de un hilo. Las corrutinas  async/await se implementan con E/S asincrónicas y sintaxis. Los módulos de Python  asyncio brindan soporte para rutinas. Las rutinas son adecuadas para tareas intensivas de E/S, como solicitudes de red, lectura y escritura de archivos , etc.

Experimento multiproceso:

import multiprocessing
import time

def count(n):
    while n > 0:
        n -= 1

def main1():
    # 单进程执行
    start_time = time.time()
    count(100000000)
    end_time = time.time()
    print("单进程执行时间:", end_time - start_time)  # 单进程执行时间: 4.279423713684082

    # 多进程执行
    start_time = time.time()
    process1 = multiprocessing.Process(target=count, args=(50000000,))
    process2 = multiprocessing.Process(target=count, args=(50000000,))
    process1.start()
    process2.start()
    process1.join()
    process2.join()
    end_time = time.time()
    print("多进程执行时间:", end_time - start_time)  # 多进程执行时间: 2.2506167888641357

if __name__ == '__main__':
    main1()

Experimento de corrutina (en este experimento, la corrutina múltiple no es dominante, porque la principal ventaja de la corrutina es que puede manejar de manera eficiente tareas intensivas de E/S, como solicitudes de red, lectura y escritura de archivos, etc. ):

import asyncio
import time

async def count(n):
    while n > 0:
        n -= 1

async def main():
    task1 = asyncio.create_task(count(50000000))
    task2 = asyncio.create_task(count(50000000))
    await asyncio.gather(task1, task2)

# 单协程执行
start_time = time.time()
asyncio.run(count(100000000))
end_time = time.time()
print("单协程执行时间:", end_time - start_time)

# 多协程执行
start_time = time.time()
asyncio.run(main())
end_time = time.time()
print("多协程执行时间:", end_time - start_time)

"""
单协程执行时间: 4.209939241409302
多协程执行时间: 4.172176122665405
"""

Subproceso del demonio del demonio de Python

  1. Demonio: un demonio es un proceso que se ejecuta en segundo plano , independientemente de la interacción del usuario. Los demonios se utilizan normalmente para realizar tareas en segundo plano, como registro, supervisión, etc. En Python,   los demonios se pueden crear utilizando atributos multiprocessing.Process de clase  .daemon

  2. Subproceso daemon: Un subproceso daemon es un subproceso que se ejecuta en segundo plano . Cuando el subproceso principal sale, el subproceso daemon se cerrará automáticamente. Los subprocesos de daemon se utilizan normalmente para realizar tareas en segundo plano, como registro, supervisión, etc. En Python,   los subprocesos de daemon se pueden crear utilizando atributos threading.Thread de clase  .daemon

Experimento del demonio:

"""
守护进程
但这段代码不适合在windows OS上运行,适合linux
"""
import os
import sys
import time


def daemonize():
    pid = os.fork()  # 创建一个新的子进程

    if pid > 0:
        sys.exit()

    os.setsid()  # 创建一个新的会话,并将子进程设置为该会话的会话领导者。这将使子进程脱离控制终端,从而实现守护进程的特性之一。
    os.umask(0)  # 设置子进程的文件创建模式

    pid = os.fork()

    if pid > 0:
        sys.exit()

    sys.stdout.flush()
    sys.stderr.flush()

    with open("/dev/null", "r") as stdin:
        os.dup2(stdin.fileno(), sys.stdin.fileno())

    with open("/dev/null", "a") as stdout:
        os.dup2(stdout.fileno(), sys.stdout.fileno())

    with open("/dev/null", "a") as stderr:
        os.dup2(stderr.fileno(), sys.stderr.fileno())


def run():
    while True:
        print("Daemon is running...")
        time.sleep(5)


if __name__ == "__main__":
    daemonize()
    run()

Experimento de subproceso de daemon:

import threading
import time


def run():
    while True:
        print("Daemon thread is running...")
        time.sleep(5)


if __name__ == "__main__":
    daemon_thread = threading.Thread(target=run)  # 线程对象
    daemon_thread.daemon = True
    daemon_thread.start()  # 启动守护线程

    # 主线程将等待10秒后结束
    time.sleep(10)
    print("Main thread is terminating")

"""
Daemon thread is running...
Daemon thread is running...
Main thread is terminating
Daemon thread is running...
"""

modelo de pato

        Duck Typing es un concepto de programación utilizado principalmente en lenguajes de escritura dinámica como Python. La idea central del modelo de pato es centrarse en el comportamiento del objeto, más que en el tipo de objeto. En otras palabras, si un objeto camina y grazna como un pato, lo consideramos un pato y no nos importa su tipo real.

class Duck:
    def quack(self):
        return "Quack!"

class Dog:
    def quack(self):
        return "Woof!"

def make_sound(animal):
    print(animal.quack())

duck = Duck()
dog = Dog()

make_sound(duck)  # 输出 "Quack!"
make_sound(dog)   # 输出 "Woof!"

¿Por qué se inician los dos subprocesos cuando se ejecuta el servidor de ejecución?

Cuando se ejecuta bajo el comando runserver de Django, generalmente se inician dos subprocesos. Los propósitos principales de estos dos hilos son:

  1. Subproceso principal : este subproceso es responsable de procesar las solicitudes HTTP , recibir solicitudes de los clientes, llamar a las funciones de vista correspondientes para procesar las solicitudes y, finalmente, devolver las respuestas a los clientes. Durante este proceso, el subproceso principal manejará tareas como el enrutamiento de URL, la representación de plantillas y las operaciones de la base de datos.

  2. Subproceso de recarga automática : este subproceso es principalmente responsable de monitorear los archivos de código fuente en el proyecto. Cuando detecta un cambio de archivo, recarga automáticamente el proyecto para que los cambios surtan efecto inmediatamente sin reiniciar manualmente el servidor. Esto es muy útil para la depuración y las iteraciones rápidas durante el desarrollo .

        Tal diseño puede hacer que los desarrolladores sean más eficientes durante el proceso de desarrollo, porque cuando el código cambia, el servidor se recargará automáticamente sin reiniciar manualmente. Al mismo tiempo, al colocar la función de recarga automática en un subproceso separado, puede asegurarse de que el subproceso principal siempre se centre en procesar solicitudes HTTP y mejorar la velocidad de respuesta del servidor.

Supongo que te gusta

Origin blog.csdn.net/lxd_max/article/details/132092041
Recomendado
Clasificación