Sök…


Introduktion

Villkorliga variabler är användbara i fall där du vill att en tråd ska vänta på något som händer i en annan tråd. Till exempel, i ett producent / konsument scenario med en eller flera producerande trådar och en konsumerar tråd, kan villkorliga variabler användas för att signalera den konsumera tråden att ny data är tillgänglig.

Anmärkningar

Allmän process

En väntan på en villkorlig variabel (queueCond i tillverkaren / konsumentexemplet) är alltid kopplad till en mutex (könMutex i producenten / konsumentexemplet), och bör alltid kopplas till en "normal" tillståndsvariabel också (kö.empty ( ) i tillverkaren / konsumentexemplet). När den används på rätt sätt garanterar detta att inga nya uppgifter saknas i konsumenten.

I allmänhet bör processen vara:

  • För signaltråden:
    1. Lås mutex
    2. Uppdatera alla data och tillståndsvariabler
    3. Signala tillståndsvariabeln
    4. Lås upp mutex
  • För den väntande tråden:
    1. Lås mutex
    2. Gör en while ögla på tillståndsvariabeln, looping så länge uppgifterna inte är klar
    3. Vänta på tillståndsvariabeln med while slingan med pthread_cond_wait()
    4. När while loop utgångar är vi nu säkra på att nya uppgifter är klar, och att mutex är låst
    5. Gör något med uppgifterna
    6. Lås upp mutex och upprepa

Med detta schema, oavsett när signalering och väntetrådar är schemalagda, kommer väntetråden aldrig att sakna data (som i, den kommer aldrig att fastna och vänta för evigt med giltig data redo). Detta kan realiseras genom att manuellt försöka köra igenom stegen för signaltråden, registrera tillstånden för mutex-, tillstånds- och tillståndsvariabler för varje steg i väntetråden.

pthread_cond_wait och mutex

För att underlätta ovanstående process krävs det att ringa pthread_cond_wait() med mutex låst. När du pthread_cond_wait() kommer pthread_cond_wait() att låsa upp mutexen innan du lägger tråden i vila, och, strax innan den återvänder av någon anledning, kommer mutexen att låsas om. Detta innebär också att om någon annan tråd för närvarande har mutex låst, kommer pthread_cond_wait() att vänta på att mutexen ska låses upp, och tills den vänta tråden faktiskt kan skaffa mutex - kommer den att strida på den tillsammans med andra trådar som försöker låsa mutexen samtidigt.

Fantastiska vakningar

Dessutom kan det verka som om while slingan väntar på tillståndsvariabeln kan ersätta ett enkelt if uttalande. Men while loopen behövs, eftersom Posix-standarden tillåter pthread_cond_wait() att göra så kallade "falska" vakningar under väntan, utan att faktiskt signaleras. Således måste koden kontrollera tillståndsvariabeln för att se om pthread_cond_wait() returneras på grund av att det faktiskt har signalerats, eller på grund av en av dessa falska vakningar.

Producent / konsumentexempel

pthread_mutex_t queueMutex;
pthread_cond_t queueCond;
Queue queue;

void Initialize() {
    //Initialize the mutex and the condition variable
    pthread_mutex_init(&queueMutex, NULL);
    pthread_cond_init(&queueCond, NULL);
}

void Producer() {
    //First we get some new data
    Data *newData = MakeNewData();

    //Lock the queue mutex to make sure that adding data to the queue happens correctly
    pthread_mutex_lock(&queueMutex);

    //Push new data to the queue
    queue.push(newData);

    //Signal the condition variable that new data is available in the queue
    pthread_cond_signal(&queueCond);

    //Done, unlock the mutex
    pthread_mutex_unlock(&queueMutex);
}


void Consumer() {

    //Run the consumer loop
    while(1) {

        //Start by locking the queue mutex
        pthread_mutex_lock(&queueMutex);

        //As long as the queue is empty,
        while(queue.empty()) {
            // - wait for the condition variable to be signalled
            //Note: This call unlocks the mutex when called and
            //relocks it before returning!
            pthread_cond_wait(&queueCond, &queueMutex);
        }

        //As we returned from the call, there must be new data in the queue - get it,
        Data *newData = queue.front();
        // - and remove it from the queue
        queue.pop();

        //Now unlock the mutex
        pthread_mutex_unlock(&queueMutex);
        
        // - and process the new data
        ProcessData(newData);
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow