1

私はC++ 11で書いた単純なタスクグループクラスで途方に暮れています.heisenbugのようにクラッシュしています(通常、システム負荷が高く、スレッドスケジューリングの圧力が高いと、メモリアクセスエラーが発生します)。最初のコード:

class task_group
{
    typedef std::promise<void> p_type;
    typedef std::future<void> f_type;
    typedef std::pair<p_type,f_type> pair_type;
    typedef std::forward_list<pair_type> container_type;
    template <typename Callable>
    struct task_wrapper
    {
        template <typename T>
        explicit task_wrapper(T &&c, pair_type &pair):m_c(std::forward<T>(c)),m_pair(pair)
        {
            m_pair.second = m_pair.first.get_future();
        }
        void operator()()
        {
            try {
                m_c();
                m_pair.first.set_value();
            } catch (...) {
                m_pair.first.set_exception(std::current_exception());
            }
        }
        Callable    m_c;
        pair_type   &m_pair;
    };
public:
    task_group() = default;
    ~task_group()
    {
        wait_all();
    }
    task_group(const task_group &) = delete;
    task_group(task_group &&) = delete;
    task_group &operator=(const task_group &) = delete;
    task_group &operator=(task_group &&) = delete;
    template <typename Callable>
    void add_task(Callable &&c)
    {
        m_container.emplace_front(p_type{},f_type{});
        task_wrapper<typename std::decay<Callable>::type> tw(std::forward<Callable>(c),m_container.front());
        std::thread thr(std::move(tw));
        thr.detach();
    }
    void wait_all()
    {
        std::for_each(m_container.begin(),m_container.end(),[this](pair_type &p) {
            if (p.second.valid()) {
                p.second.wait();
            }
        });
    }
    void get_all()
    {
        std::for_each(m_container.begin(),m_container.end(),[this](pair_type &p) {
            if (p.second.valid()) {
                p.second.get();
            }
        });
    }
private:
    container_type m_container;
};

タスク (nullary 呼び出し可能オブジェクトで表される) はadd_task()メソッドを介して追加できます。このメソッドは、すぐにデタッチされる別のスレッドで呼び出し可能オブジェクトを起動します。callable は実際にはラッパー構造にラップされており、callable が作業を終了したら promise の値を設定するだけです。Promise と Future は、スレッドの存続時間とは無関係に永続的な .xml に格納されforward_listます。

クラスを作成してタスクを追加し、完了を待つだけです。ばかげた例 (単体テストから):

task_group tg;
std::vector<unsigned> values(100u);
for (auto i = 0u; i < 100u; ++i) {
    auto s = boost::lexical_cast<std::string>(1u);
    BOOST_CHECK_NO_THROW(tg.add_task([&values,s,i](){values[i] += boost::lexical_cast<unsigned>(s);}));
}
tg.wait_all();
BOOST_CHECK((std::all_of(values.begin(),values.end(),[](unsigned n){return n == 1u;})));

valgrind を使用してテスト ケースを実行しようとしましたが、共有ポインターの参照カウントに関する誤検出しか得られないようです。クラスは非常に単純なので、私は本当にばかげたことをしていると確信していますが、promise と future について多くのグーグル検索を行っても、私の側で重大な誤用があったことを示唆するものはありませんでした。

4

0 に答える 0