Как проверить, завершены ли задачи в io_service? - PullRequest
0 голосов
/ 29 мая 2019

У меня вопрос по поводу boost::io_service.

У меня есть набор задач, которые я могу выполнять одновременно. После запуска всех из них мне нужно запустить другой набор задач одновременно. Однако первый набор должен быть завершен до запуска второго набора. Это означает, что мне нужно убедиться, что все задания, отправленные в io_service, выполнены, прежде чем начинать планировать второй набор.

Я могу реализовать это, сохранив какой-то счетчик и добавив цикл занятости, но он не выглядит очень эффективным. Итак, я хотел проверить, есть ли у кого-то лучшая идея или нет. Ниже приведен фиктивный код, который я использовал для экспериментов.

Заранее спасибо!

 #include <cstdio>
 #include <iostream>
 #include <unistd.h>
 #include <boost/asio/io_service.hpp>
 #include <boost/bind.hpp>
 #include <boost/thread/thread.hpp>

 const size_t numTasks = 100000;

 void print_counter(const size_t id)
 {
   if (id + 1 == numTasks) {
     printf("sleeping for %ld\n", id);
     sleep(15);
   }
   printf("%ld\n", id);
 }

 int main(int argc, char** argv)
 {
   using namespace std;
   using namespace boost;

   asio::io_service io_service;
   asio::io_service::work work(io_service);

   const size_t numWorker = 4;
   boost::thread_group workers;
   for(size_t i = 0; i < numWorker; ++i) {
     workers.create_thread(boost::bind(&asio::io_service::run, &io_service));
   }

   for(size_t i = 0; i < numTasks; ++i) {
     io_service.post(boost::bind(print_counter, i));
   }

   // TODO: wait until all the tasks are done above


   for(size_t i = 0; i < numTasks; ++i) {
     io_service.post(boost::bind(print_counter, i));
   }

   // TODO: wait until all the tasks are done above

   // ...

   // Finally stop the service
   io_service.stop();
   workers.join_all();
   return 0;
 }

Ответы [ 2 ]

2 голосов
/ 29 мая 2019

Ваша основная проблема в том, что все наборы ваших задач обрабатываются одним и тем же экземпляром io_service.Функция io_service::run возвращает, где нет задач для обработки.Деструктор io_service::work сообщает io_service объекту, что run может вернуться, если в очереди нет ожидающих выполнения задач.Вы можете опубликовать все задачи из первого набора, затем уничтожить работу и подождать, пока не вернется io_service::run, затем снова создать объект work, опубликовать задачи из следующего набора и удалить работу и т. Д.Для этого просто напишите вспомогательный класс, который может выглядеть примерно так:

class TasksWaiter 
{
public:
    TasksWaiter(int numOfThreads) 
    {
        work = std::make_unique<boost::asio::io_service::work>(io_service);
        for(size_t i = 0; i < numOfThreads; ++i) {
            workers.create_thread(boost::bind(&boost::asio::io_service::run, &io_service));
        }
    }

    ~TasksWaiter() {
        work.reset();
        workers.join_all();
    }

    template<class F>
    void post(F f) {
        io_service.post(f);
    }

    boost::thread_group workers;
    boost::asio::io_service io_service;
    std::unique_ptr<boost::asio::io_service::work> work;
};

int main()
{
    {
        TasksWaiter w1{4};
        for (int i = 0; i < numTasks; ++i)
            w1.post(boost::bind(print_counter,i));
        // work in w1 is destroyed, then io_service::run ends 
        // when there are no tasks to be performed
    }
    printf("wait here");
    {
        TasksWaiter w1{4};
        for (int i = 0; i < numTasks; ++i)
            w1.post(boost::bind(print_counter,i));
    }
}

несколько замечаний:

  1. в конструкторе создан пул потоков

  2. в работе деструктора удаляется, поэтому io_service::run возвращается, только если нет ожидающих задач

  3. функциональность деструктора может быть включена в функцию-член - например,wait, тогда вам не нужно использовать {} scope для ожидания ваших задач.

0 голосов
/ 29 мая 2019

С документация io_service::run :

Функция run() блокирует до тех пор, пока вся работа не будет завершена и больше не будет обработчиковотправляется или до тех пор, пока io_context не будет остановлено.

Кроме того, из io_context::work документация для конструктора :

Используется конструкторсообщить io_context, что некоторые работы начались.Это гарантирует, что функция run() объекта io_context не будет закрыта, пока работа ведется .

[Выделение шахты]

Короче говоря, если функция run возвращает значение и stopped возвращает значение false, то вся работа завершена.

...