2009-09-11 14 views
17

Szukam sposobu oczekiwania na wiele zmiennych warunkowych. tj. coś takiego:czeka na wiele zmiennych warunkowych w boost?

boost::condition_variable cond1; 
boost::condition_variable cond2; 

void wait_for_data_to_process() 
{ 
    boost::unique_lock<boost::mutex> lock(mut); 

    wait_any(lock, cond1, cond2); //boost only provides cond1.wait(lock); 

    process_data(); 
} 

Jest to możliwe dzięki zmiennym warunku. A jeśli nie, istnieją alternatywne rozwiązania?

Dzięki

Odpowiedz

11

Nie wierzę, że możesz zrobić coś takiego z boost :: thread. Być może dlatego, że zmienne warunkowe POSIX nie dopuszczają tego typu konstrukcji. Oczywiście, Windows ma WaitForMultipleObjects w formie opublikowanej, co może być rozwiązaniem, jeśli chcesz ograniczyć swój kod do prymitywów synchronizacji systemu Windows.

Inną opcją byłoby użycie mniejszej liczby zmiennych warunkowych: wystarczy mieć 1 zmienną warunku, którą uruchamiasz, gdy wydarzy się coś "interesującego". Następnie, za każdym razem, gdy chcesz czekać, uruchamiasz pętlę, która sprawdza, czy pojawiła się twoja szczególna sytuacja, a jeśli nie, wróć do oczekiwania na zmienną warunku. Trzeba czekać na tych zmiennych stanu, w takiej pętli tak czy inaczej, jako warunek zmiennych czeka podlegają fałszywych wybudzeń (od docs boost :: wątku, podkreślenie moje):

void wait(boost::unique_lock<boost::mutex>& lock)
...
efekty:
Atomowo zadzwoń pod numer lock.unlock() i zablokuj bieżący wątek. Wątek zostanie odblokowany po otrzymaniu powiadomienia przez wywołanie this->notify_one() lub this->notify_all() lub podświadomie. ...

+4

Wydaje mi się, że posiadanie trzeciej zmiennej warunkowej dla "1 lub 2 zmienionych" jest rzeczywiście najlepszym podejściem. Innym podejściem jest przekonwertowanie warunku oczekiwania na warunek na oczekiwanie deskryptorów plików za pomocą select i użycie potoków do komunikacji wątków. Ponieważ to pytanie jest ogólne (nie wyjaśnia, na co czeka się), nie jest jasne, czy wybór będzie lepszy. –

+0

Problem z użyciem jednej zmiennej warunkowej to enkapsulacja. Nie możesz mieć żadnych funkcji, takich jak 'GoStyleChannel :: receive_but_break_if (condition_variable & cv, function & pred)'. Innymi słowy, załóżmy, że masz jakąś funkcję blokującą, która już używa zmiennej warunku. Chcesz go zablokować, dopóki nie stanie się warunek * lub * twój stan się stanie. Nie możesz tego zrobić bez posiadania globalnej 'std :: condition_variable somethingHasHappenedSomewhere', która jest naprawdę bzdura. – Timmmm

0
alternative solutions? 

nie jestem pewien biblioteki Boost, ale można użyć funkcji WaitForMultipleObjects czekać na wielu obiektach jądra. Sprawdź, czy to pomaga.

+0

Tak, wiem o WaitForMultipleObject. Szukam odpowiednika doładowania. Ale wygląda na to, że impuls nie zapewnia takiego. Zobacz: http://lists.boost.org/Archives/boost/2004/12/77175.php –

0

Managu wskazuje, że używanie wielu warunków może nie być dobrym rozwiązaniem. To, co chcesz zrobić, powinno być możliwe do zrealizowania za pomocą Semaforów.

11

Jako że Managu już odpowiedział, możesz użyć tej samej zmiennej warunkowej i sprawdzić wiele "zdarzeń" (zmiennych bool) w pętli while. Jednak równoczesny dostęp do tych zmiennych boolowych musi być chroniony przy użyciu tego samego muteksu, który jest używany przez funkcję warunkową.

Ponieważ już przeszedł trud wpisując ten kod przykładowy dla powiązanego question, ja to odśwież tutaj:

boost::condition_variable condvar; 
boost::mutex mutex; 
bool finished1 = false; 
bool finished2 = false; 

void longComputation1() 
{ 
    { 
     boost::lock_guard<boost::mutex> lock(mutex); 
     finished1 = false; 
    } 
    // Perform long computation 
    { 
     boost::lock_guard<boost::mutex> lock(mutex); 
     finished1 = true; 
    } 
    condvar.notify_one(); 
} 

void longComputation2() 
{ 
    { 
     boost::lock_guard<boost::mutex> lock(mutex); 
     finished2 = false; 
    } 
    // Perform long computation 
    { 
     boost::lock_guard<boost::mutex> lock(mutex); 
     finished2 = true; 
    } 
    condvar.notify_one(); 
} 

void somefunction() 
{ 
    // Wait for long computations to finish without "spinning" 
    boost::lock_guard<boost::mutex> lock(mutex); 
    while(!finished1 && !finished2) 
    { 
     condvar.wait(lock); 
    } 

    // Computations are finished 
} 
+0

+1 To jest dobre rozwiązanie – Alexey

0

Używając tych samych warunkach zmiennej dla wielu zdarzeń technicznie działa, ale nie robi” t umożliwić hermetyzację. Tak więc podjąłem próbę stworzenia klasy, która je obsługuje. Jeszcze nie testowane! Również nie obsługuje notify_one(), ponieważ nie wymyśliłem, jak to zaimplementować.

#pragma once 

#include <condition_variable> 
#include <unordered_set> 

// This is like a `condition_variable` but you can wait on multiple `multi_condition_variable`s. 
// Internally it works by creating a new `condition_variable` for each `wait_any()` and registering 
// it with the target `multi_condition_variable`s. When `notify_all()` is called, the main `condition_variable` 
// is notified, as well as all the temporary `condition_variable`s created by `wait_any()`. 
// 
// There are two caveats: 
// 
// 1. You can't call the destructor if any threads are `wait()`ing. This is difficult to get around but 
//  it is the same as `std::wait_condition` anyway. 
// 
// 2. There is no `notify_one()`. You can *almost* implement this, but the only way I could think to do 
//  it was to add an `atomic_int` that indicates the number of waits(). Unfortunately there is no way 
//  to atomically increment it, and then wait. 
class multi_condition_variable 
{ 
public: 
    multi_condition_variable() 
    { 
    } 

    // Note that it is only safe to invoke the destructor if no thread is waiting on this condition variable. 
    ~multi_condition_variable() 
    { 
    } 

    // Notify all threads calling wait(), and all wait_any()'s that contain this instance. 
    void notify_all() 
    { 
     _condition.notify_all(); 
     for (auto o : _others) 
      o->notify_all(); 
    } 

    // Wait for notify_all to be called, or a spurious wake-up. 
    void wait(std::unique_lock<std::mutex>& loc) 
    { 
     _condition.wait(loc); 
    } 

    // Wait for any of the notify_all()'s in `cvs` to be called, or a spurious wakeup. 
    static void wait_any(std::unique_lock<std::mutex>& loc, std::vector<std::reference_wrapper<multi_condition_variable>> cvs) 
    { 
     std::condition_variable c; 
     for (multi_condition_variable& cv : cvs) 
      cv.addOther(&c); 
     c.wait(loc); 
     for (multi_condition_variable& cv : cvs) 
      cv.removeOther(&c); 
    } 

private: 
    void addOther(std::condition_variable* cv) 
    { 
     std::lock_guard<std::mutex> lock(_othersMutex); 
     _others.insert(cv); 
    } 

    void removeOther(std::condition_variable* cv) 
    { 
     // Note that *this may have been destroyed at this point. 
     std::lock_guard<std::mutex> lock(_othersMutex); 
     _others.erase(cv); 
    } 

    // The condition variable. 
    std::condition_variable _condition; 

    // When notified, also notify these. 
    std::unordered_set<std::condition_variable*> _others; 

    // Mutex to protect access to _others. 
    std::mutex _othersMutex; 
}; 

// Example use: 
// 
// multi_condition_variable cond1; 
// multi_condition_variable cond2; 
// 
// void wait_for_data_to_process() 
// { 
//  unique_lock<boost::mutex> lock(mut); 
// 
//  multi_condition_variable::wait_any(lock, {cond1, cond2}); 
// 
//  process_data(); 
// } 
Powiązane problemy