Aprendizaje de subprocesos múltiples de C++ 05 bloqueo de tiempo de espera, bloqueo recursivo y bloqueo compartido

1. Bloqueo temporizado Timed_mutex

Función: evita interbloqueos a largo plazo, puede registrar la adquisición de bloqueos, múltiples tiempos de espera, puede registrar registros y obtener condiciones de error

En 04, debido a que try_lock() no bloqueará el subproceso pero siempre ocupa recursos de CPU, agregue sleep_for (100ms) para retrasar y bloquear el subproceso por un tiempo para dar una oportunidad a otros subprocesos, pero este retraso se llama this_thread La siguiente función:

if (!mux.try_lock())
        {
    
    
            cout << "." << flush;
            this_thread::sleep_for(100ms);
            continue;
        }

También puede usar el retraso como un parámetro del constructor de bloqueo, usando timed_mutex:

timed_mutex tmux;

void ThreadMainTime(int i)
{
    
    


    for (;;)
    {
    
    
        if (!tmux.try_lock_for(chrono::milliseconds(500)))
        {
    
     
            //如果未在规定时间内拿到锁,那么这段代码可能会出现问题,这里可以进行日志的写入,便于调试
            cout << i << "[try_lock_for timeout]" << endl;
            continue;
        }
        cout << i << "[in]" << endl;
        this_thread::sleep_for(2000ms);
        tmux.unlock();
        this_thread::sleep_for(1ms);
    }
}

De manera similar, para garantizar que el desbloqueo pueda liberar recursos, finalmente retrase:

Se crean tres subprocesos, y cada subproceso se bloquea durante 500 ms después de intentar desbloquearlos.Pregunta
: ¿Por qué los tres subprocesos se separan entre sí y la impresión no es desordenada?

int main(int argc, char* argv[])
{
    
    
 
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadMainTime, i + 1);
        th.detach();
    }
    getchar();
  }

[in] indica que el subproceso ha ingresado
[try_lock_for timeout] indica que la entrada falló
inserte la descripción de la imagen aquí

2. Bloqueo recursivo (bloqueo reentrante) recursive_mutex

El mismo bloqueo se puede bloquear varias veces en el mismo hilo. Evite algunos
escenarios de uso de puntos muertos innecesarios: se llama a una función de forma recursiva y la función accede a los datos compartidos para bloquear, se llama a sí misma de forma recursiva antes de desbloquear y se utilizará el bloqueo recursivo. En otro escenario, en una clase, dos funciones miembro f1 y f2 accederán a datos compartidos y estarán bloqueadas. Llamar a f2 después de bloquear en f1 también requiere un bloqueo recursivo. La implicación es que siempre que se llame a la operación de bloqueo del mismo bloqueo antes del desbloqueo, para evitar un punto muerto, se requiere un bloqueo recursivo.

Referencia: https://www.zhihu.com/question/448190927/answer/1768286353

recursive_mutex rmux;
void Task1()
{
    
    
    rmux.lock();
    cout << "task1 [in]" << endl;
    rmux.unlock();
}
void Task2()
{
    
    
    rmux.lock();
    cout << "task2 [in]" << endl;
    rmux.unlock();
}
void ThreadMainRec(int i)
{
    
    
    for (;;)
    {
    
    
        rmux.lock(); 
        Task1();
        cout << i << "[in]" << endl;
        this_thread::sleep_for(2000ms);
        Task2();
        rmux.unlock();
        this_thread::sleep_for(1ms);
    }
}

Incluso si se trata de un bloqueo recursivo, debe desbloquearse varias veces después de bloquearse.

int main(int argc, char* argv[])
{
    
    
    
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadMainRec, i + 1);
        th.detach();
    }
    getchar();
 }  

Se puede ver que ThreadMainRec No. 123 se cambia alternativamente, y cada ThreadMainRec ejecuta task1 y task2 una vez
inserte la descripción de la imagen aquí

3. Bloqueo compartido shared_mutex

A menudo nos encontramos con este escenario:
muchos (100) subprocesos leen un recurso al mismo tiempo, no habrá ningún problema. Cuando un subproceso modifica este recurso, estos 100 recursos deben esperar. En este momento, hay otros recursos que quieren modificar este recurso. La modificación del recurso también debe esperar

En resumen:
el subproceso que lee recursos puede ser leído por otros subprocesos cuando lee, pero no puede escribir.
El subproceso que escribe recursos no puede leer ni escribir cuando otros subprocesos están escribiendo.
Por lo tanto, subprocesos con diferentes necesidades usan diferentes bloqueos y subprocesos que leen usan bloqueos que son leídos. El hilo de escritura usa el bloqueo de escritura. Si
un hilo solo está leyendo, usa el bloqueo de lectura. Si
un hilo va a escribir, primero usa el bloqueo de lectura, luego el bloqueo de escritura y luego modifica el recurso.

Ahora sacrifique estos dos candados:
c++14 mutex compartido temporizado shared_timed_mutex
c++17 mutex compartido shared_mutex

Tome el estándar C++14 como ejemplo:
shared_timed_mutex stmux,
bloqueo para escritura: stmux.lock(),
bloqueo para lectura: stmux.lock_shared();

shared_timed_mutex stmux;

void ThreadRead(int i)
{
    
    
    for (;;)
    {
    
    
        stmux.lock_shared();
        cout << i << " Read" << endl;
        //this_thread::sleep_for(500ms);
        stmux.unlock_shared();
        this_thread::sleep_for(1ms);
    }
}
void ThreadWrite(int i)
{
    
    
    for (;;)
    {
    
    
        stmux.lock(); //互斥锁 写入
        cout << i << " Write" << endl;
        this_thread::sleep_for(300ms);
        stmux.unlock();
        this_thread::sleep_for(1ms);
    }
}
int main(int argc, char* argv[])
{
    
    
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadWrite, i + 1);
        th.detach();
    }
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadRead, i + 1);
        th.detach();
    }
    getchar();
    return 0;
}

Si desea obtener el bloqueo de escritura, primero debe asegurarse de que se libere el bloqueo de lectura y el bloqueo de escritura. Si
desea obtener el bloqueo de lectura, solo necesita asegurarse de que se libere el bloqueo de escritura.
Puede ver que el subproceso de lectura a menudo aparece en la misma fila, y los tres subprocesos de lectura pasan Después de que se crea el ciclo for, si el recurso no está bloqueado por el bloqueo de escritura, los tres subprocesos pueden tomar el bloqueo de lectura e ingresar a la tarea de subproceso de cout juntos Cuando un hilo cout << i << " Read" << endl ;Cuando la ejecución no ha terminado (más de una línea después del ensamblado), otro hilo cout << i << " Read" << endl; es enviado a la CPU para que se ejecute, por lo que aparecerá el fenómeno de la misma línea, pero el subproceso de escritura no. Quiere asegurarse de que no haya subprocesos de lectura o subprocesos actualmente. shared_lock C ++ 14 implementa un contenedor de propiedad mutex compartida
inserte la descripción de la imagen aquí
extraíble :

int main(int argc, char* argv[])
{
    
    

    {
    
    
        //共享锁
        static shared_timed_mutex  tmux;
        //读取锁 共享锁
        {
    
    
            //调用共享锁 
            shared_lock<shared_timed_mutex> lock(tmux);
            cout << "read data" << endl;
            //退出栈区 释放共享锁
        }
        //写入锁 互斥锁
        {
    
    
            unique_lock<shared_timed_mutex> lock(tmux);
            cout << "write data" << endl;
        }
    }
    return 0;
}

shared_lock<shared_timed_mutex> lock(tmux); bloqueo compartido en el mutex entrante en el constructor

explicit shared_lock(mutex_type& _Mtx)
        : _Pmtx(_STD addressof(_Mtx)), _Owns(true) {
    
     // construct with mutex and lock shared
        _Mtx.lock_shared();
    }

bloqueo_único<shared_timed_mutex> lock(tmux); Mutex en el mutex entrante en el constructor

explicit unique_lock(_Mutex& _Mtx) : _Pmtx(_STD addressof(_Mtx)), _Owns(false) {
    
     // construct and lock
        _Pmtx->lock();
        _Owns = true;
    }

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_42567607/article/details/125530759
Recomendado
Clasificación