В настоящее время у меня есть синхронный код C ++, который последовательно выполняет некоторые трудоемкие задачи. Я рассматриваю возможность использования сопрограмм Boost для преобразования этого в параллельные задачи.
Ядро задач — это вызов внешней библиотеки, которая предоставляет либо синхронный API (который она использует в настоящее время), либо асинхронный API, который должен периодически опрашиваться как для выполнения действия, так и для определения его завершения. API не является поточно-ориентированным, но если используется асинхронная версия, он может обрабатывать несколько запросов одновременно, если сам API опроса не вызывается одновременно.
(Поскольку я не хочу никакого смещения потоков, похоже, что я должен использовать API сопрограммы напрямую, а не использовать обертки ASIO или аналогичные, но я хочу быть убежденным в противном случае. Я также использую Boost 1.55 на данный момент , поэтому библиотека Fiber недоступна, но это также кажется излишним для этого использования.)
Мой главный вопрос с сопрограммами на данный момент заключается в том, что я не уверен, как реализовать регулирование при опросе — скажу, что у меня есть 100 задач, которые должны выполняться параллельно; Я хочу, чтобы он выполнил один опрос через все 100 задач, а затем спит поток в течение указанного времени. Некоторые задачи могут быть выполнены раньше, чем другие, поэтому в дальнейшем они все равно могут опрашивать 40 задач за цикл и затем спать столько же времени. Я не хочу, чтобы он когда-либо опрашивал одну и ту же задачу дважды подряд без промежуточного сна.
Хорошо, оказалось намного проще, чем я думал. Это то, что я закончил до сих пор:
typedef boost::coroutines::coroutine<void>::push_type coroutine;
typedef boost::coroutines::coroutine<void>::pull_type yield_context;
typedef std::vector<coroutine> coroutine_list;
bool run_once_all(coroutine_list& coros)
{
bool pending = false;
for (auto& coro : coros)
{
if (coro)
{
coro();
pending = pending || !coro.empty();
}
}
return pending;
}
void run_all(coroutine_list& coros,
const boost::chrono::nanoseconds& idle_ns)
{
while (run_once_all(coros))
{
boost::this_thread::sleep_for(idle_ns);
}
}
void run_all(coroutine_list& coros, yield_context& yield)
{
while (run_once_all(coros))
{
yield();
}
}
Идея в том, что вы делаете coroutine_list
, затем emplace_back
лямбда-функции с подписью void (yield_context&)
, затем run_all
, Первый предназначен для верхнего уровня, а второй — для вложенных сопрограмм.
Кажется, работает, хотя я еще не дал ему большую тренировку. На самом деле немного удивлен, что-то вроде этого уже нет в библиотеке.
Других решений пока нет …