Я не могу заставить код работать надежно в простом консольном приложении VS2012, состоящем из производителя и потребителя, который использует условную переменную C ++ 11. Я стремлюсь создать небольшую надежную программу (для использования в качестве основы для более сложной программы), которая использует метод с тремя аргументами wait_for или, возможно, метод wait_until из кода, который я собрал на этих веб-сайтах:
condition_variable:
ждать,
Подожди пока
Я хотел бы использовать 3 аргумента wait_for с предикатом, как показано ниже, за исключением того, что для этого мне понадобится переменная члена класса, которая будет наиболее полезной для меня позже. Я получаю «Письмо о нарушении прав доступа 0x__«или» Недопустимый параметр был передан службе или функции «как ошибки после примерно минуты работы.
Будет ли устойчивый_клок и 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 других важных обновлений нет. Я использую Windows 7 32-разрядную версию.
К сожалению, это на самом деле ошибка в реализации VS_ conditioniable на VS2012, и исправление не будет исправлено. Вам придется обновить его до VS2013, когда он будет выпущен.
Увидеть:
http://connect.microsoft.com/VisualStudio/feedback/details/762560
Прежде всего, при использовании condition_variable
s я лично предпочитаю некоторые классы обертки как 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 #, или может быть не так эффективно, как следовало бы, но оно помогает мне.
Во-вторых, когда мне нужно реализовать производящую / потребляющую идиому, я пытаюсь использовать параллельную реализацию очереди (например, Очередь) или напишите один для себя. Но вы также должны рассмотреть возможность сделать все правильно, используя Шаблон активного объекта. Но для простого решения мы можем использовать это:
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;
}