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

Edw*_*ard 6 c++ multithreading condition-variable c++11 visual-studio-2012

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

condition_variable: wait_for, wait_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;
}
Run Code Online (Sandbox Code Playgroud)

Visual Studio Desktop Express安装了1个重要更新,Windows Update没有其他重要更新.我正在使用Windows 7 32位.

bre*_*anw 5

遗憾的是,这实际上是VS2012实现condition_variable的一个错误,并且修补程序不会被修补.你必须在它发布时升级到VS2013.

看到:

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


zah*_*hir 0

首先,在使用condition_variables 时,我个人更喜欢一些包装类,例如AutoResetEventC# 中的:

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;
};
Run Code Online (Sandbox Code Playgroud)

这可能与 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;
};
Run Code Online (Sandbox Code Playgroud)

这个类有一些异常安全问题,并且缺少方法的常量性,但就像我说的,对于一个简单的解决方案来说,这应该适合。

因此,修改后的代码如下所示:

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;
}
Run Code Online (Sandbox Code Playgroud)