Procesos y subprocesos de Qt: sincronización de subprocesos, reentrada y seguridad de subprocesos

1. Método de hilo síncrono

El propósito de usar subprocesos es permitir que el código se ejecute en paralelo, pero a veces los subprocesos deben detenerse y esperar a otros subprocesos. Por ejemplo, si dos subprocesos intentan escribir en la misma variable al mismo tiempo, el resultado no está definido, por lo que los subprocesos deben sincronizarse. La sincronización de subprocesos es una técnica común para proteger datos como los recursos compartidos. El principio de obligar a un subproceso a esperar a otro se denomina exclusión mutua.

Las clases QMutex, QReadWriteLock, QSemaphore y QWaitCondition en Qt proporcionan métodos para sincronizar subprocesos.

  • QMutex proporciona un bloqueo de exclusión mutua (mutex) y, como máximo, un subproceso puede adquirir la exclusión mutua en cualquier momento. Si un subproceso intenta adquirir una exclusión mutua mientras la exclusión mutua ya está bloqueada, el subproceso dormirá hasta que el subproceso que ahora adquiere la exclusión mutua la desbloquee. Las exclusiones mutuas se utilizan a menudo para proteger el acceso a datos compartidos (por ejemplo, datos a los que pueden acceder varios subprocesos simultáneamente).
  • QReadWriteLock es un bloqueo de lectura y escritura, que es muy similar a QMutex, excepto que divide el acceso a los datos compartidos en acceso de "lectura" y acceso de "escritura", lo que permite que varios subprocesos "lean" el acceso a los datos simultáneamente. El uso de QReadWriteLock en lugar de QMutex cuando sea posible puede mejorar la concurrencia de programas de subprocesos múltiples.
  • QSemaphore es un semáforo, que es una generalización de QMutex y se usa para proteger una cierta cantidad de los mismos recursos, mientras que un mutex mutex solo puede proteger un recurso. La sincronización de subprocesos de Qt (modo consumidor productor - QSemaphore) proporciona un caso típico, semáforo: acceso síncrono al búfer circular entre "productor-consumidor".
  • QWaitCondition es una variable de condición que permite que un subproceso active otros subprocesos cuando se cumplen algunas condiciones. Se pueden bloquear uno o más subprocesos esperando que QWaitCondition establezca una condición para wakeOne() o wakeAll(). Utilice wakeOneO para activar un subproceso en espera seleccionado al azar y use wakeAll() para activar todos los subprocesos en espera.

Los beneficios de este artículo, la tarifa para recibir el paquete de materiales de aprendizaje de desarrollo Qt, video técnico, el contenido incluye (base del lenguaje C++, introducción a la programación Qt, mecanismo de ranura y señal QT, dibujo de imagen de desarrollo de interfaz QT, red QT, programación de base de datos QT, combate de proyecto QT, QSS, OpenCV, módulo rápido, preguntas de entrevista, etc.) ↓↓↓↓↓↓ Vea a continuación

2. Reentrada y seguridad del hilo

Los términos "reentrada" y "seguridad de subprocesos" se utilizan aquí para etiquetar clases y funciones para indicar cómo se pretende que se utilicen en aplicaciones de subprocesos múltiples.

  • Varios subprocesos pueden llamar a una función segura para subprocesos al mismo tiempo, y no hay problema incluso si la persona que llama usa datos compartidos, porque el acceso a los datos compartidos es en serie.
  • Varios subprocesos también pueden llamar a una función reentrante al mismo tiempo, pero cada llamador solo puede usar sus propios datos.

Por lo tanto, una función segura para subprocesos siempre es reentrante, pero una función reentrante no es necesariamente segura para subprocesos. Por extensión, una clase reentrante significa que sus funciones miembro pueden ser llamadas de forma segura por varios subprocesos, siempre que cada subproceso utilice un objeto diferente de esta clase. Una clase segura para subprocesos significa que sus funciones miembro pueden ser llamadas de forma segura por múltiples subprocesos, incluso si todos los subprocesos usan la misma instancia de la clase.

Nota: Algunas clases de Qt están diseñadas para ser seguras para subprocesos si su propósito es ser multiproceso. Si una función no está marcada como segura para subprocesos o reentrante, no debe ser utilizada por subprocesos diferentes. Si una clase no está marcada como segura para subprocesos o reentrante, múltiples subprocesos no deben acceder a las instancias de esa clase.

reentrada

Las clases de C++ tienden a ser reentrantes simplemente porque solo pueden acceder a sus propios datos. Cualquier subproceso puede acceder a una función miembro de una instancia de clase reentrante, siempre que ningún otro subproceso llame a la función miembro de la instancia al mismo tiempo. Por ejemplo, la siguiente clase Counter es reentrante:

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { ++n; }
    void decrement() { --n; }
    int value() const { return n; }

private:
    int n;
};

Esta clase no es segura para subprocesos porque si varios subprocesos intentan modificar el miembro de datos n, los resultados no están definidos. Esto se debe a que ni las operaciones ++ ni -- son siempre atómicas. De hecho, generalmente se expanden en 3 instrucciones de máquina:

  1. cargar el valor de la variable en el registro
  2. incrementar o decrementar el valor en un registro
  3. Escribe el valor en el registro de vuelta a la memoria

Si el subproceso A y el subproceso B cargan el valor anterior de la variable en un registro al mismo tiempo, incrementan el valor en el registro y lo vuelven a escribir en la memoria, eventualmente se sobrescribirán entre sí, ¡lo que hará que el valor de la variable se incremente solo una vez!

seguridad del hilo

Obviamente, el acceso debe ser serial: el hilo A debe ejecutar 1.2.3.tres pasos (atomicidad) sin interrupción, y luego el hilo B puede iniciar la ejecución, y viceversa. Una forma sencilla de hacer que una clase sea segura para subprocesos es usar un QMutex para proteger todos los accesos a los miembros de datos.

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { QMutexLocker locker(&mutex); ++n; }
    void decrement() { QMutexLocker locker(&mutex); --n; }
    int value() const { QMutexLocker locker(&mutex); return n; }

private:
    mutable QMutex mutex;
    int n;
};

La clase QMutexLocker bloquea automáticamente el mutex en su constructor y lo desbloquea cuando se llama al destructor. Bloquear la exclusión mutua garantiza que el acceso de otros subprocesos se serializará. El miembro de datos mutex se declara mutable porque value() es una función const en la que necesitamos bloquear y desbloquear el mutex.

Notas sobre las clases de Qt

Muchas clases de Qt son reentrantes, pero no seguras para subprocesos, porque la seguridad de subprocesos significa agregar una sobrecarga adicional para bloquear y desbloquear un QMutex. Por ejemplo: QString es reentrante pero no seguro para subprocesos. Puede acceder a diferentes instancias de QString desde varios subprocesos al mismo tiempo, pero no puede acceder a la misma instancia de QString desde varios subprocesos al mismo tiempo (a menos que use QMutex para proteger el acceso).

Algunas clases y funciones de Qt son seguras para subprocesos. Son principalmente clases relacionadas con subprocesos (p. ej., QMutex) y algunas funciones básicas (p. ej., QCoreApplication::postEvent()).

El artículo se transfiere desde blog garden (fengMisaka): proceso Qt y subproceso tres: sincronización de subprocesos, reentrada y seguridad de subprocesos - fengMisaka - blog garden

Los beneficios de este artículo, la tarifa para recibir el paquete de materiales de aprendizaje de desarrollo Qt, video técnico, el contenido incluye (base del lenguaje C++, introducción a la programación Qt, mecanismo de ranura y señal QT, dibujo de imagen de desarrollo de interfaz QT, red QT, programación de base de datos QT, combate de proyecto QT, QSS, OpenCV, módulo rápido, preguntas de entrevista, etc.) ↓↓↓↓↓↓ Vea a continuación

Supongo que te gusta

Origin blog.csdn.net/QtCompany/article/details/131871929
Recomendado
Clasificación