5

我無法在包含使用C++ 11條件變量的生產者和使用者的簡單VS2012控制檯應用程序中可靠地運行代碼。我的目標在生產,使用3個參數wait_for方法或者可能wait_until方法從代碼中,我在這些網站已經聚集了小可靠的方案(作爲一個更復雜的程序的基礎上使用):在VS2012中使用C++ 11條件變量

condition_variablewait_forwait_until

我想使用3參數wait_for與下面的謂詞,除了它需要使用類成員變量對我以後最有用。我在收到「訪問衝突寫入位置0x_ _」或「一個無效的參數已傳遞給服務或功能」作爲錯誤後大約只有一分鐘的運行。

steady_clock和2參數wait_until是否足以替換3參數wait_for?我也嘗試過沒有成功。

有人可以展示如何獲得下面的代碼無限期運行,沒有錯誤或怪異的行爲,從夏令時或互聯網時間同步更改掛鐘時間?

指向可靠示例代碼的鏈接可能同樣有用。

// ConditionVariable.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

#include <condition_variable> 
#include <mutex> 
#include <thread> 
#include <iostream> 
#include <queue> 
#include <chrono> 
#include <atomic> 

#define TEST1 

std::atomic<int> 
//int 
    qcount = 0; //= ATOMIC_VAR_INIT(0); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::queue<int> produced_nums; 
    std::mutex m; 
    std::condition_variable cond_var; 
    bool notified = false; 
    unsigned int count = 0; 

    std::thread producer([&]() { 
     int i = 0; 
     while (1) { 
      std::this_thread::sleep_for(std::chrono::microseconds(1500)); 
      std::unique_lock<std::mutex> lock(m); 
      produced_nums.push(i); 
      notified = true; 
      qcount = produced_nums.size(); 
      cond_var.notify_one(); 
      i++; 
     } 
     cond_var.notify_one(); 
    }); 

    std::thread consumer([&]() { 
     std::unique_lock<std::mutex> lock(m); 
     while (1) { 
#ifdef TEST1 
      // Version 1 
      if (cond_var.wait_for(
       lock, 
       std::chrono::microseconds(1000), 
       [&]()->bool { return qcount != 0; })) 
      { 
       if ((count++ % 1000) == 0) 
        std::cout << "consuming " << produced_nums.front () << '\n'; 
       produced_nums.pop(); 
       qcount = produced_nums.size(); 
       notified = false; 
      } 
#else 
      // Version 2 
      std::chrono::steady_clock::time_point timeout1 = 
       std::chrono::steady_clock::now() + 
       //std::chrono::system_clock::now() + 
       std::chrono::milliseconds(1); 

      while (qcount == 0)//(!notified) 
      { 
       if (cond_var.wait_until(lock, timeout1) == std::cv_status::timeout) 
        break; 
      } 

      if (qcount > 0) 
      { 
       if ((count++ % 1000) == 0) 
       std::cout << "consuming " << produced_nums.front() << '\n'; 
       produced_nums.pop(); 
       qcount = produced_nums.size(); 
       notified = false; 
      } 
#endif 
     } 
    }); 

    while (1); 
    return 0; 
} 

Visual Studio Desktop Express有1個重要更新,它安裝了並且Windows Update沒有其他重要更新。我正在使用Windows 7 32位。

回答

1

首先,在使用condition_variable的I個人比較喜歡一些包裝類像AutoResetEvent從C#:

struct AutoResetEvent 
{ 
    typedef std::unique_lock<std::mutex> Lock; 

    AutoResetEvent(bool state = false) : 
     state(state) 
    { } 

    void Set() 
    { 
     auto lock = AcquireLock(); 
     state = true; 
     variable.notify_one(); 
    } 

    void Reset() 
    { 
     auto lock = AcquireLock(); 
     state = false; 
    } 

    void Wait(Lock& lock) 
    { 
     variable.wait(lock, [this]() { return this->state; }); 
     state = false; 
    } 

    void Wait() 
    { 
     auto lock = AcquireLock(); 
     Wait(lock); 
    } 

    Lock AcquireLock() 
    { 
     return Lock(mutex); 
    } 
private: 

    bool state; 
    std::condition_variable variable; 
    std::mutex mutex; 
}; 

這可能不是相同的行爲與C#類型或可能不會被視爲有效,因爲它應該是但它爲我完成了一些事情。其次,當我需要實現一個生產/消費習慣用法時,我嘗試使用併發隊列實現(例如,tbb queue)或爲我自己寫一個。但你也應該考慮通過使用Active Object Pattern來做正確的事情。但對於簡單的解決方案,我們可以利用這一點:

template<typename T> 
struct ProductionQueue 
{ 
    ProductionQueue() 
    { } 

    void Enqueue(const T& value) 
    { 
     { 
      auto lock = event.AcquireLock(); 
      q.push(value); 
     } 
     event.Set(); 
    } 

    std::size_t GetCount() 
    { 
     auto lock = event.AcquireLock(); 

     return q.size(); 
    } 

    T Dequeue() 
    { 
     auto lock = event.AcquireLock(); 
     event.Wait(lock); 

     T value = q.front(); 
     q.pop(); 

     return value; 
    } 

private: 
    AutoResetEvent event; 
    std::queue<T> q; 
}; 

這個類有一些例外,安全問題和失誤的方法常量性,但就像我說的,一個簡單的解決方案這應該適合。

因此,作爲一個結果,修改後的代碼如下所示:

int main(int argc, char* argv[]) 
{ 
    ProductionQueue<int> produced_nums; 
    unsigned int count = 0; 

    std::thread producer([&]() { 
     int i = 0; 
     while (1) { 
      std::this_thread::sleep_for(std::chrono::microseconds(1500)); 
      produced_nums.Enqueue(i); 
      qcount = produced_nums.GetCount(); 
      i++; 
     } 
    }); 

    std::thread consumer([&]() { 
     while (1) { 
      int item = produced_nums.Dequeue(); 
      { 
       if ((count++ % 1000) == 0) 
        std::cout << "consuming " << item << '\n'; 
       qcount = produced_nums.GetCount(); 
      } 
     } 
    }); 

    producer.join(); 
    consumer.join(); 

    return 0; 
} 
+0

你的代碼不使用'wait_for'或'wait_until',因此不能解決OP的問題。 –

4

可悲的是,這其實是在VS2012的實施condition_variable的錯誤,並修復不會在修補你必須升級。到VS2013發佈時。

參見:

http://connect.microsoft.com/VisualStudio/feedback/details/762560

+0

這不會使vs2012對任何多線程應用程序/庫無用(更糟糕但有害)嗎?如果他們提供的唯一解決方案就是不使用他們自己的產品(而不是使用boost condition_variable等),那麼我會非常失望。不幸的是,我們在vs2012上有一個大到中等的代碼庫,最近我剛剛計劃轉換爲std :: condition_variable,std :: mutex等,而不是boost。這是我計劃的結束。 – Mert

+0

確實。 MS沒有動力讓人們保持舊版本的軟件。當我幾年前遇到類似的情況時,我們只是被迫使用替代品。 – brendanw