2009-09-11 37 views
17

Tôi đang tìm cách đợi nhiều biến điều kiện. tức là. một cái gì đó như:chờ đợi cho nhiều biến điều kiện trong tăng?

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(); 
} 

Có điều gì đó tương tự với các biến điều kiện. Và nếu không có giải pháp thay thế?

Cảm ơn

Trả lời

11

Tôi không tin rằng bạn có thể làm bất cứ điều gì như thế này với boost :: thread. Có lẽ vì biến điều kiện POSIX không cho phép loại cấu trúc này. Tất nhiên, Windows có WaitForMultipleObjects là aJ được đăng, đây có thể là giải pháp nếu bạn sẵn sàng hạn chế mã của bạn đối với các nguyên tắc đồng bộ hóa Windows.

Một tùy chọn khác sẽ sử dụng ít biến điều kiện hơn: chỉ có 1 biến điều kiện mà bạn kích hoạt khi có bất kỳ điều gì "thú vị" xảy ra. Sau đó, bất cứ lúc nào bạn muốn đợi, bạn chạy một vòng lặp kiểm tra xem tình huống cụ thể của bạn có quan tâm không, và nếu không, hãy quay lại chờ biến số điều kiện. Bạn nên chờ đợi vào các biến trạng thái trong một vòng lặp như vậy dù sao, như chờ đợi biến tình trạng này tùy thuộc vào wakeups giả mạo (từ docs boost :: chủ đề, tôi nhấn mạnh):

void wait(boost::unique_lock<boost::mutex>& lock)
...
Effects:
Gọi theo nguyên tắc lock.unlock() và chặn luồng hiện tại. Chuỗi sẽ bỏ chặn khi được thông báo bằng cách gọi tới số this->notify_one() hoặc this->notify_all() hoặc giả mạo. ...

+4

Dường như với tôi rằng có một biến điều kiện thứ ba cho "hoặc 1 hoặc 2 thay đổi" thực sự là cách tiếp cận tốt nhất. Một cách tiếp cận khác là chuyển đổi trạng thái chờ trong tình trạng chờ đợi trên các bộ mô tả tập tin bằng cách chọn và sử dụng các đường ống cho giao tiếp luồng. Vì câu hỏi này là chung chung (nó không giải thích những gì đang chờ đợi), nên không rõ là chọn sẽ tốt hơn. –

+0

Vấn đề với việc sử dụng một biến điều kiện là đóng gói. Bạn không thể có bất kỳ chức năng nào như 'GoStyleChannel :: receive_but_break_if (điều kiện_variable & cv, hàm & pred)'. Nói cách khác, giả sử bạn có một số chức năng chặn đã sử dụng biến điều kiện. Bạn muốn nó chặn cho đến khi điều kiện xảy ra * hoặc * tình trạng của bạn xảy ra. Bạn không thể làm điều đó mà không có một 'std :: condition_variable toàn cục somethingHasHappenedSomewhere', điều này thực sự là crap. – Timmmm

0
alternative solutions? 

Tôi không chắc chắn của thư viện Boost nhưng bạn có thể sử dụng chức năng WaitForMultipleObjects chờ cho nhiều đối tượng hạt nhân. Chỉ cần kiểm tra nếu điều này giúp.

+0

Vâng tôi biết WaitForMultipleObject. Tôi đang tìm kiếm tương đương trong tăng. Nhưng có vẻ như tăng không cung cấp một. Xem: http://lists.boost.org/Archives/boost/2004/12/77175.php –

0

Do Managu chỉ ra rằng việc sử dụng nhiều điều kiện có thể không phải là giải pháp tốt ngay từ đầu. Những gì bạn muốn làm nên có thể được thực hiện bằng cách sử dụng Semaphores.

11

Khi người quản lý đã trả lời, bạn có thể sử dụng cùng biến điều kiện và kiểm tra nhiều "sự kiện" (biến bool) trong vòng lặp while. Tuy nhiên, đồng thời truy cập vào các biến bool này phải được bảo vệ bằng cách sử dụng cùng một mutex mà condvar sử dụng.

Kể từ khi tôi đã đi qua những rắc rối của gõ mã ví dụ này cho một liên quan question, tôi sẽ repost nó ở đây:

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 đây là giải pháp tốt – Alexey

0

Sử dụng biến tình trạng tương tự cho nhiều sự kiện về mặt kỹ thuật công trình, nhưng nó doesn' t cho phép đóng gói. Vì vậy, tôi đã có một nỗ lực để làm cho một lớp học hỗ trợ nó. Chưa được thử nghiệm! Ngoài ra, nó không hỗ trợ notify_one() vì tôi chưa tìm ra cách thực hiện điều đó.

#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(); 
// } 
Các vấn đề liên quan