Как создать очередь, которая содержит boost :: packaged_task <> с функциями, которые возвращают произвольные типы? - PullRequest
4 голосов
/ 05 мая 2011

Я пытаюсь создать рабочую очередь функций, которые должны выполняться одним потоком и могут передаваться многими потоками.Для этого я планировал использовать boost :: packaged_task и boost :: unique_future.Идея состоит в том, что вы должны сделать:

Foo value = queue.add (myFunc) .get ();

, которая будет блокироваться, пока не будет выполнена функция.Таким образом, queue.add (...) принимает boost :: function и возвращает boost :: unique_future.Затем он создает boost :: packaged_task с использованием boost :: function для своего конструктора.

Проблема, с которой я сталкиваюсь, заключается в том, что boost :: function <...> не всегда будет одинаковойвремя.В частности, возвращаемое значение для него изменится (однако функции никогда не будут принимать никаких параметров).Таким образом, у меня должна быть функция добавления, которая выглядит примерно так:

template <typename ResultType>
boost::unique_future<ResultType> add(boost::function<ResultType ()> f) {
   boost::packaged_task<boost::function<ResultType ()> > task(f);
   queue.push_back(task);
   return task.get_future();
}

Ладно, это не так уж и плохо, но потом я столкнулся с проблемой определения «очереди».Я думаю, что у меня нет выбора, кроме как использовать boost :: any, так как типы не будут постоянными:

std::list<boost::any> queue; // note: I'm not concerned with thread-safety yet

Но затем я сталкиваюсь с проблемой, когда пытаюсь реализовать свой executeSingle (занимает всего одинпункт из очереди для выполнения):

void executeSingle() {
    boost::any value = queue.back();
    boost::packaged_task<?> task = boost::packaged_task<?>(boost::move(value));
    // actually execute task
    task();
    queue.pop_back();
}

'?'обозначить то, в чем я не уверен.Я не могу вызвать executeSingle с шаблоном, так как он вызывается из отдельного потока.Я попытался использовать boost :: any, но я получаю сообщение об ошибке:

  conversion from 'boost::any' to non-scalar type  boost::detail::thread_move_t<boost:thread>' requested.

Самое смешное, что на самом деле меня не волнует тип возвращаемого значения packaged_task, я просто хочу его выполнить, но я могу разобраться в деталях шаблона.

Любое понимание будет с благодарностью!

Ответы [ 3 ]

5 голосов
/ 05 мая 2011

Вы должны хранить boost::function<void()>. Обратите внимание, что boost::packaged_task<R>::operator() ничего не возвращает; он заполняет связанный boost::future. На самом деле, даже если он вернул что-то, вы все равно можете использовать boost::function<void()>, так как вы все равно не будете заинтересованы в возвращаемом значении: все, что вас волнует, это вызвать queue.back()(). Если бы это было так, boost::function<void()>::operator() позаботится об отбрасывании возвращенного значения для вас.

Как незначительное примечание, вы можете изменить сигнатуру вашего add метода для шаблонного типа Functor, а не boost::function, и использовать boost::result_of, чтобы получить тип результата для boost::packaged_task.

Мое предложение в целом:

template<typename Functor>
boost::future<typename boost::result_of<Functor()>::type>
queue::add(Functor functor) // assuming your class is named queue
{
    typedef typename boost::result_of<Functor()>::type result_type;
    boost::packaged_task<result_type> task(functor);
    boost::unique_future<result_type> future = task.get_future();
    internal_queue.push_back(boost::move(task)); // assuming internal_queue member
    return boost::move(future);
}

void
queue::executeSingle()
{
    // Note: do you really want LIFO here?
    queue.back()();
    queue.pop_back();
}

EDIT

Как заботиться о семантике перемещения внутри queue::add

typedef typename boost::result_of<Functor()>::type result_type;
typedef boost::packaged_task<result_type> task_type;
boost::shared_ptr<task_type> task = boost::make_shared<task_type>(functor);
boost::unique_future<result_type> future = task->get_future();

/* boost::shared_ptr is possibly move-enabled so you can try moving it */
internal_queue.push_back( boost::bind(dereference_functor(), task) );

return boost::move(future);

, где dereference_functor может быть:

struct dereference_functor {
    template<typename Pointer>
    void
    operator()(Pointer const& p) const
    {
        (*p)();
    }
};

Вы также можете заменить выражение bind на более понятное

boost::bind(&task_type::operator(), task)

, который также не требует специального функтора. Однако, если есть несколько перегрузок task_type::operator(), это может потребовать устранения неоднозначности; код может также сломаться, если в будущем изменение в Boost.Thread приведет к перегрузке.

1 голос
/ 05 мая 2011

Вы используете старомодные виртуальные функции.Определите базовый класс task_base с помощью метода virtual execute, затем определите производный от шаблона класс, который содержит конкретный экземпляр задачи.Что-то в этом роде:

struct task_base {
  virtual void execute() = 0;
};
template<typename ResultType>
struct task_holder : task_base {
  task_holder(boost::packaged_task<boost::function<ResultType ()> >&& task)
    : m_task(task) { }
  void execute() {
    m_task();
  }
private:
  boost::packaged_task<boost::function<ResultType ()> > m_task;
};

И определите свою очередь для хранения unique_ptr<task_base>.По сути, это то, что делает boost::any, только вы будете использовать определенную функцию, а именно execute.

ПРИМЕЧАНИЕ: непроверенный код!И я до сих пор не очень знаком со ссылками.Это просто, чтобы дать вам представление о том, как будет выглядеть код.

0 голосов
/ 06 сентября 2013

Несколько запоздало, но вы, возможно, захотите использовать Boost.Asio вместо использования собственного решения для работы с очередями.

Хотя эта библиотека выросла как библиотека ввода-вывода, она поддерживает асинхронные вызовы, как это. Просто определите где-нибудь io_service, запустите его внутри потока, а затем post функторы для вызова в этом потоке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...