Разработка библиотеки параллелизма C ++ с «фьючерсами» или схожей парадигмой - PullRequest
3 голосов
/ 03 июня 2011

Я работаю над проектом C ++, которому нужно выполнить много заданий в пуле потоков. Задания подвержены сбоям, что означает, что мне нужно знать, как завершается каждое задание после его завершения. Будучи программистом на Java по большей части, мне нравится идея использовать "futures" или подобную парадигму, сродни различным классам в пакете util.concurrent в Java.

У меня два вопроса: во-первых, существует ли что-то подобное для C ++ (я ничего не нашел в Boost, но, возможно, я не выгляжу достаточно усердно); и во-вторых, это даже нормальная идея для C ++?

Я нашел краткий пример того, что я пытаюсь сделать здесь:

http://www.boostcookbook.com/Recipe:/1234841

Имеет ли этот подход смысл?

Ответы [ 3 ]

5 голосов
/ 03 июня 2011

В Boost реализовано фьючерсов и других инструментов потоков, .

Обратите внимание, что при вызове метода get() для boost::unique_future он будет перебрасывать все исключения, которые могли бытьхранится внутри него во время асинхронного выполнения.

Я бы посоветовал вам сделать что-то вроде:

#pragma once

#include <tbb/concurrent_queue.h>

#include <boost/thread.hpp>
#include <boost/noncopyable.hpp>

#include <functional>

namespace internal
{
    template<typename T>
    struct move_on_copy
    {
        move_on_copy(const move_on_copy<T>& other) : value(std::move(other.value)){}
        move_on_copy(T&& value) : value(std::move(value)){}
        mutable T value;
    };

    template<typename T>
    move_on_copy<T> make_move_on_copy(T&& value)
    {
        return move_on_copy<T>(std::move(value));
    }
}

class executor : boost::noncopyable
{
    boost::thread thread_;

    tbb::concurrent_bounded_queue<std::function<void()>> execution_queue_;

    template<typename Func>
    auto create_task(Func&& func) -> boost::packaged_task<decltype(func())> // noexcept
    {   
        typedef boost::packaged_task<decltype(func())> task_type;

        auto task = task_type(std::forward<Func>(func));            

        task.set_wait_callback(std::function<void(task_type&)>([=](task_type& my_task) // The std::function wrapper is required in order to add ::result_type to functor class.
        {
            try
            {
                if(boost::this_thread::get_id() == thread_.get_id())  // Avoids potential deadlock.
                    my_task();
            }
            catch(boost::task_already_started&){}
        }));

        return std::move(task);
    }

public:

    explicit executor() // noexcept
    {
        thread_ = boost::thread([this]{run();});
    }

    ~executor() // noexcept
    {   
        execution_queue_.push(nullptr); // Wake the execution thread.
        thread_.join();
    }

    template<typename Func>
    auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept
    {   
        // Create a move on copy adaptor to avoid copying the functor into the queue, tbb::concurrent_queue does not support move semantics.
        auto task_adaptor = internal::make_move_on_copy(create_task(func));

        auto future = task_adaptor.value.get_future();

        execution_queue_.push([=]
        {
            try{task_adaptor.value();}
            catch(boost::task_already_started&){}
        });

        return std::move(future);       
    }

    template<typename Func>
    auto invoke(Func&& func) -> decltype(func()) // noexcept
    {
        if(boost::this_thread::get_id() == thread_.get_id())  // Avoids potential deadlock.
            return func();

        return begin_invoke(std::forward<Func>(func), prioriy).get();
    }

private:

    void run() // noexcept
    {
        while(true)
        {
            std::function<void()> func;
            execution_queue_.pop(func); 
                    if(!func)
                       break;
            func();
        }
    }   
};
5 голосов
/ 03 июня 2011

Фьючерсы присутствуют как в готовящемся стандарте (C ++ 0x), так и внутри boost.Обратите внимание, что хотя основное имя future совпадает, вам нужно прочитать документацию, чтобы найти другие типы и понять семантику.Я не знаю фьючерсов на Java, поэтому я не могу сказать вам, где они отличаются, если они имеют.определение этой части стандарта.Он также написал C ++ Concurrency in Action , в котором содержится хорошее описание будущего, задач, обещаний и связанных с ними объектов.Его компания также продает полную и вплоть до реализации библиотек потоков C ++ 0x, если вам интересно.

0 голосов
/ 03 июня 2011

C ++ шаблоны менее строгие, чем Java Generics, поэтому «будущие» могут быть легко перенесены с ними и примитивами синхронизации потоков. Что касается существующих библиотек, которые поддерживают такой механизм, надеюсь, кто-то еще знает об этом.

...