6

C++11 条件変数を使用するプロデューサーとコンシューマーで構成される単純な VS2012 コンソール アプリケーションでコードを確実に動作させることができません。私は、これらの Web サイトで収集したコードから、3 つの引数の wait_for メソッドまたはおそらく wait_until メソッドを使用する、信頼性の高い小さなプログラム (より複雑なプログラムの基礎として使用するため) を作成することを目指しています。

condition_variable : wait_forwait_until

以下のような述語で3つの引数wait_forを使用したいと思いますが、後で最も役立つようにクラスメンバー変数を使用する必要があります。約 1 分間実行しただけで、 「アクセス違反書き込み場所 0x_ _」または「無効なパラメーターがサービスまたは関数に渡されました」というエラーが表示されます。

3 つの引数 wait_for を置き換えるには、steady_clock と 2 つの引数 wait_until で十分でしょうか? 私もこれを試しましたが成功しませんでした。

以下のコードを無期限に実行する方法を誰かが示すことができますか?

信頼できるサンプル コードへのリンクも同様に役立ちます。

// 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 ビットを使用しています。

4

2 に答える 2

0

まず、 s を使用している間、私は個人的にC#condition_variableのようないくつかのラッパー クラスを好みます。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 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;
};

このクラスにはいくつかの例外的な安全性の問題があり、メソッドの const 性が失われていますが、私が言ったように、単純なソリューションにはこれが適しているはずです。

その結果、変更されたコードは次のようになります。

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;
}
于 2013-01-12T23:46:44.903 に答える