0
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <iostream> 

std::mutex globalMutex; 
std::condition_variable globalCondition; 
int global = 0; 
int activity = 0; 
int CountOfThread = 1; // or more than 1 

// just for console display, not effect the problem 
std::mutex consoleMutex; 

void producer() { 
    while (true) { 
     { 
      std::unique_lock<std::mutex> lock(globalMutex); 
      while (activity == 0) { 
       lock.unlock(); 
       std::this_thread::yield(); 
       lock.lock(); 
      } 
      global++; 
      globalCondition.notify_one(); 
     } 
     std::this_thread::yield(); 
    } 
} 


void customer() { 
    while (true) { 
     int x; 
     { 
      std::unique_lock<std::mutex> lock(globalMutex); 
      activity++; 
      globalCondition.wait(lock); // <- problem 
      activity--; 
      x = global; 
     } 
     { 
      std::lock_guard<std::mutex> lock(consoleMutex); 
      std::cout << x << std::endl; 
     } 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 
    } 
} 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    for (int i = 0; i < CountOfThread; ++i) { 
     std::thread(customer).detach(); 
    } 
    std::thread(producer).detach(); 
    getchar(); 
    return 0; 
} 

что я хочу, чтобы каждый раз, когда есть поток клиентов, чтобы получить увеличенный глобальный, ожидайте отображения как: 1, 2, 3, ..., но то, что я вижу, глобальное значение будет увеличено между ожиданием и активностью - таким образом, фактический дисплей: 1, 23, 56, 78, ....C++: condition-variable wait

Я выяснил, что проблема в ожидании(), acutully there 3 этапа ожидания(), «разблокировка, ожидание, блокировка», между сигналами (wait return) и mutex.lock, это не атомная операция, поток производителя может блокировать mutex перед wait() для блокировки мьютекса, а активность по-прежнему не равна нулю, поэтому глобальное увеличение будет неожиданно

есть ли способ убедиться, что я ожидаю?

+0

Поздравляет на размещение полного минимальный примера. Это очень помогает! –

ответ

0

Я нахожу, что это помогает мне в отношении темы. Например, если вы были клиентом, что такое вы ожидания? Я имею в виду в контексте темы. Когда вы так думаете, это очень упрощает код с monitors.

Теперь, как сказал Мартин, я считаю, что повторное обращение к thread.yield немного страшно. Это может привести к страшному перемежению кода.

Чтобы показать пример, почему ваш код не работает, давайте пройдем через быстрое чередование:

  1. Несколько клиентов созданы, и тот, который захватывает увеличение блокировки activity. Затем этот поток переходит в режим сна из-за вызова wait.
  2. Другая цепочка просыпается после вызова wait начальным потоком клиента. Это связано с тем, что wait разблокирует мьютекс, переданный в него.
  3. Этот поток приобретает globalMutex и увеличивается activity. Затем он ждет.
  4. Повторите для CountOfThread количество потоков, поскольку это вполне возможно с помощью многопоточного кода.
  5. Наконец, поток производителя работает, видит activity == 0 и разблокируется. Однако это не было notify_one (то есть сигнал). Тогда это дает. Это может привести к неопределенному и запутанному коду.

Мои предложения:

  1. Никогда не называйте yield во время ожидания на состояние. Это может привести к сложному и трудночитаемому неверному коду монитора.
  2. Подумайте, какое состояние ожидает каждый поток. Если разные разделы кода ждут в разных условиях, создайте другую блокировку.Используйте только один замок для одного фрагмента кода.
  3. Используйте различные переменные условия для разных условий при определенных обстоятельствах. Если условие зависит от разных данных, обязательно используйте переменную условия.

В ваших обстоятельствах решение не так сложно, как вы думаете. Используйте мое первоначальное предложение: подумайте в контексте потока. Например, когда поток производителей работает, хочет подождать, когда клиент не заметил, что global был изменен. Когда поток клиентов работает, он хочет ждать, когда производитель не изменил global.

Вот пример поведения вы желаете:

mutex m; 

condition_variable cv; 
int global = 0, prev_global = 0; 

void producer() 
{ 
    while (true) 
    { 
     unique_lock<mutex> lock(m); 

     while (prev_global != global) 
     { 
      cv.wait(lock); 
     } 
     prev_global = global++; 
     cv.notify_one(); 
     lock.unlock(); 
    } 
} 

void customer() 
{ 
    while (true) 
    { 
     unique_lock<mutex> lock(m); 

     while (prev_global == global) 
     { 
      cv.wait(lock); 
     } 

     prev_global = global; 
     cv.notify_one(); 
     lock.unlock(); 
    } 
} 

int main() 
{ 
    vector<thread> pool; 
    for (int i = 0; i < 5; ++i) 
    { 

     pool.push_back(thread (customer)); 
    } 

    pool.push_back(thread (producer)); 

    for (auto it = pool.begin(); it != pool.end(); ++it) 
    { 
     it->join(); 
    } 
    return 0; 
} 
+0

очень полезно, это действительно то, что мне нужно, thx много. – Benjamin

+0

@Benjamin великий, рад, что я мог бы помочь! Вы отметили бы вопрос, как ответили? –

1

Ваша проблема в том, что когда activity is> 0, producer может циклически захватывать блокировку, увеличивая глобальность и уведомляя переменную условия. (Уведомление не должно иметь соответствующего официанта).

Повторные вызовы thread.yield - это немного красный флаг - они означают, что вы проводите опрос, а не ожидаете. Я думаю, что решение состоит в том, что вам нужны переменные состояния. продюсер ждет один, пока не будет уведомлен потребителем, потребитель ждет от другого, пока он не будет уведомлен производителем. Я не совсем уверен, как вы делаете эту работу с несколькими потребителями.

 Смежные вопросы

  • Нет связанных вопросов^_^