在VS2012中使用C++11条件变量

Using a C++11 condition variable in VS2012

本文关键字:C++11 条件 变量 VS2012      更新时间:2023-10-16

我无法让代码在一个简单的VS2012控制台应用程序中可靠地工作,该应用程序由使用C++11条件变量的生产者和消费者组成。我的目标是制作一个可靠的小程序(用作更复杂程序的基础),该程序使用我在以下网站上收集的代码中的3参数wait_fo方法或wait_until方法:

条件变量(_V):wait_ fo,等待直到

我想将3参数wait_fo与下面这样的谓词一起使用,但它需要使用一个类成员变量才能在以后对我最有用。运行大约一分钟后,我收到"写入位置0x__的访问违规"或"向服务或函数传递了无效参数"作为错误。

steady_clock和2参数wait_until是否足以替换3参数wait_fo?我也尝试过,但没有成功。

有人能展示如何让下面的代码无限期地运行,而不会出现错误或奇怪的行为,无论是从夏令时还是互联网时间同步改变墙上的时钟时间吗?

链接到可靠的示例代码也同样有用。

// 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位。

遗憾的是,这实际上是VS2012的condition_variable实现中的一个错误,修复程序不会被修补。当它发布时,您必须升级到VS2013。

参见:

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

首先,在使用condition_variable时,我个人更喜欢一些包装类,如C#中的AutoResetEvent

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队列)或为自己编写一个。但是您也应该考虑通过使用活动对象模式来使事情变得正确。但对于简单的解决方案,我们可以使用这个:

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