Domanda

Dato il seguente frammento di codice C++11:

#include <condition_variable>
#include <mutex>
std::mutex block;
long count;
std::condition_variable cv;

void await()
{
    std::unique_lock<std::mutex> lk(block);
    if (count > 0)
        cv.wait(lk);
}

void countDown()
{
    std::lock_guard<std::mutex> lk(block);
    if (count > 0)
    {
        count--;
        if (count==0) cv.notify_all();
    }
}

Se non è chiaro cosa sto cercando di realizzare, desidero che le chiamate attendano per mettere in pausa il thread chiamante mentre il conteggio è maggiore di 0 e, se è già stato ridotto a zero, non dovrebbe essere affatto messo in pausa.Altri thread possono chiamare countDown() che riattiverà tutti i thread che avevano precedentemente chiamato wait.

Il codice sopra sembra funzionare in tutti i casi che ho provato, ma ho questo fastidioso dubbio al riguardo, perché mi sembra che ci sia la possibilità di un comportamento imprevisto se il thread che chiama wait() viene immediatamente anticipato dopo che il suo test di condizione è stato valutato e appena prima che il thread venga effettivamente sospeso dalla chiamata cv.wait(), e se la funzione countDown viene chiamata in questo momento e il conteggio è uguale a 0, allora invierà una notifica al variabile di condizione, SE in realtà lo stavano già aspettando...ma il thread che chiama wait non ha ancora raggiunto la chiamata cv.wait(), quindi quando il thread che chiama wait riprende, si ferma alla chiamata cv.wait() e aspetta indefinitamente.

In realtà non l'ho ancora visto accadere nella pratica, ma vorrei rafforzare il codice contro questa eventualità.

È stato utile?

Soluzione

È positivo che tu stia pensando a queste possibilità.Ma in questo caso il tuo codice è corretto e sicuro.

Se await viene anticipato immediatamente dopo che il suo test di condizione è stato valutato e appena prima che il thread venga effettivamente sospeso dalla chiamata cv.wait(), e se la funzione countDown viene chiamata in questo momento, quest'ultimo thread si bloccherà mentre tenta di ottenere il block mutex fino a quando await effettivamente chiama cv.wait(lk).

La chiamata a cv.wait(lk) rilascia implicitamente il blocco block, e quindi ora un altro thread può ottenere il blocco block In countDown().E finché un filo tiene fermo il lucchetto block In countDown() (anche dopo cv.notify_all() si chiama), il await il thread non può tornare da cv.wait().IL await il thread si blocca implicitamente quando si tenta di ribloccarlo block durante il ritorno da cv.wait().

Aggiornamento

IO fatto commetti un errore da principiante durante la revisione del tuo codice <blush>.

cv.wait(lk) potrebbe tornare spuriamente.Cioè, potrebbe ritornare anche se non è stato notificato.Per proteggerti da questo dovresti posizionare il tuo wait sotto un ciclo while, invece che sotto un if:

void await()
{
    std::unique_lock<std::mutex> lk(block);
    while (count > 0)
        cv.wait(lk);
}

Ora se l'attesa ritorna in modo spurio, ricontrolla la condizione e, se ancora non soddisfatta, attende nuovamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top