История изменений
Исправление Manhunt, (текущая версия) :
таск, выполняясь в треде может сам просить таскманагера дать другой таск (для него это сабтаск), сетнуть его, подписать свой каллбек в сабтаск и отправить сабтаск в тредпул. причем сам таск из треда выходит и будет довызван по завершении сабтаска.
Угу, это можно делать посредством коллбэков. И роль таскманагера тут вырожденная.
К примеру, есть таска #1 - приготовить пирог. Чтобы пирог приготовить, сперва нужно поискать ингридиенты, так что вместо таски #1 создаём подтаски для поиска ингридиентов #2 и #3. Поиск ингридиентов выполняется наперегонки. Когда все ингридиенты найдены (обе таски #2 и #3 завершились), можно заняться собственно готовкой - это будет таска #4. Когда готовка завершена - можно уведомиьт заказчика таски #1, что всё готово.
Накидал тут на коленке (скорее всего код бажный, но идею +/- иллюстрирует):
#include <functional>
#include <memory>
#include <atomic>
#include <iostream>
#include <boost/asio.hpp>
std::unique_ptr<boost::asio::thread_pool> g_pool;
template<class Task>
void schedule(Task task)
{
boost::asio::post(*g_pool, task);
}
template<typename Arg1, typename Arg2>
class CollectAndSchedule: public std::enable_shared_from_this< CollectAndSchedule<Arg1, Arg2> >
{
typedef CollectAndSchedule<Arg1, Arg2> This;
typedef std::function<void(Arg1, Arg2)> Task;
std::atomic<int> args_count;
Task task;
Arg1 arg1;
Arg2 arg2;
void run_task() { task(arg1, arg2); }
void set_arg1(Arg1 arg) { arg1 = arg; on_arg_set(); }
void set_arg2(Arg2 arg) { arg2 = arg; on_arg_set(); }
void on_arg_set()
{
if(++args_count == 2)
{
schedule(std::bind(&This::run_task, this->shared_from_this()));
}
}
public:
template<class T>
CollectAndSchedule(T t): args_count(0), task(t) {}
std::function<void(Arg1)> make_arg1_callback()
{
using namespace std::placeholders;
return std::bind(&This::set_arg1, this->shared_from_this(), _1);
}
std::function<void(Arg2)> make_arg2_callback()
{
using namespace std::placeholders;
return std::bind(&This::set_arg2, this->shared_from_this(), _1);
}
};
struct Cat{ };
struct Dog{ void eat(Cat) {} };
struct Pie{ void cook(Cat, Dog) {} };
// a task
void catch_cat(std::function<void(Cat)> result_cb)
{
Cat cat; // find a cat
std::cout << "[Task #2] Got a cat!" << std::endl;
result_cb(cat); // pass the cat to consumer
}
// a task
void catch_dog(std::function<void(Dog)> result_cb)
{
Dog dog; // find a dog
std::cout << "[Task #3] Got a dog!" << std::endl;
result_cb(dog); // pass the dog to consumer
}
// a task
void bloody_business(std::function<void(Pie)> result_cb, Cat cat, Dog dog)
{
Pie pie;
std::cout << "[Task #4] Cooking a pie!" << std::endl;
pie.cook(cat, dog);
result_cb(pie); // pass the pie to consumer
}
// a task
void make_pie(std::function<void(Pie)> result_cb)
{
std::cout << "[Task #1] Got an order for a pie, but need ingridients first!" << std::endl;
using namespace std::placeholders;
auto subtasks_joiner = std::make_shared< CollectAndSchedule<Cat, Dog> >(
std::bind(bloody_business, result_cb, _1, _2)
);
schedule(std::bind(catch_cat, subtasks_joiner->make_arg1_callback()));
schedule(std::bind(catch_dog, subtasks_joiner->make_arg2_callback()));
}
void happy_with_a_pie(Pie)
{
std::cout << "Got a pie!" << std::endl;
}
int main()
{
// init
g_pool = std::make_unique<boost::asio::thread_pool>();
schedule(std::bind(make_pie, happy_with_a_pie));
// shutdown
g_pool->join();
g_pool.reset();
}
Исходная версия Manhunt, :
таск, выполняясь в треде может сам просить таскманагера дать другой таск (для него это сабтаск), сетнуть его, подписать свой каллбек в сабтаск и отправить сабтаск в тредпул. причем сам таск из треда выходит и будет довызван по завершении сабтаска.
Угу, это можно делать посредством коллбэков. И роль таскманагера тут вырожденная.
К примеру, есть таска #1 - приготовить пирог. Чтобы пирог приготовить, сперва нужно поискать ингридиенты, так что вместо таски #1 создаём подтаски для поиска ингридиентов #2 и #3. Поиск ингридиентов выполняется наперегонки. Когда все ингридиенты найдены (обе таски #2 и #3 завершились), можно заняться собственно готовкой - это будет таска #4. Когда готовка завершена - можно уведомиьт заказчика таски #1, что всё готово.
Накидал тут на коленке (скорее всего код бажный, но идею +/- иллюстрирует):
#include <functional>
#include <memory>
#include <atomic>
#include <iostream>
#include <boost/asio.hpp>
std::unique_ptr<boost::asio::thread_pool> g_pool;
template<class Task>
void schedule(Task task)
{
boost::asio::post(*g_pool, task);
}
template<typename Arg1, typename Arg2>
class CollectAndSchedule: public std::enable_shared_from_this< CollectAndSchedule<Arg1, Arg2> >
{
typedef CollectAndSchedule<Arg1, Arg2> This;
typedef std::function<void(Arg1, Arg2)> Task;
std::atomic<int> args_count;
Task task;
Arg1 arg1;
Arg2 arg2;
void run_task() { task(arg1, arg2); }
void set_arg1(Arg1 arg) { arg1 = arg; on_arg_set(); }
void set_arg2(Arg2 arg) { arg2 = arg; on_arg_set(); }
void on_arg_set()
{
if(++args_count == 2)
{
schedule(std::bind(&This::run_task, this->shared_from_this()));
}
}
public:
template<class T>
CollectAndSchedule(T t): args_count(0), task(t) {}
std::function<void(Arg1)> make_arg1_callback()
{
using namespace std::placeholders;
return std::bind(&This::set_arg1, this->shared_from_this(), _1);
}
std::function<void(Arg2)> make_arg2_callback()
{
using namespace std::placeholders;
return std::bind(&This::set_arg2, this->shared_from_this(), _1);
}
};
struct Cat{ };
struct Dog{ void eat(Cat) {} };
struct Pie{ void cook(Cat, Dog) {} };
// a task
void catch_cat(std::function<void(Cat)> result_cb)
{
Cat cat; // find a cat
std::cout << "[Task #2] Got a cat!" << std::endl;
result_cb(cat); // pass the cat to consumer
}
// a task
void catch_dog(std::function<void(Dog)> result_cb)
{
Dog dog; // find a dog
std::cout << "[Task #3] Got a dog!" << std::endl;
result_cb(dog); // pass the dog to consumer
}
// a task
void bloody_business(std::function<void(Pie)> result_cb, Cat cat, Dog dog)
{
Pie pie;
std::cout << "[Task #4] Cooking a pie!" << std::endl;
pie.cook(cat, dog);
result_cb(pie); // pass the dog to consumer
}
// a task
void make_pie(std::function<void(Pie)> result_cb)
{
std::cout << "[Task #1] Got an order for a pie, but need ingridients first!" << std::endl;
using namespace std::placeholders;
auto subtasks_joiner = std::make_shared< CollectAndSchedule<Cat, Dog> >(
std::bind(bloody_business, result_cb, _1, _2)
);
schedule(std::bind(catch_cat, subtasks_joiner->make_arg1_callback()));
schedule(std::bind(catch_dog, subtasks_joiner->make_arg2_callback()));
}
void happy_with_a_pie(Pie)
{
std::cout << "Got a pie!" << std::endl;
}
int main()
{
// init
g_pool = std::make_unique<boost::asio::thread_pool>();
schedule(std::bind(make_pie, happy_with_a_pie));
// shutdown
g_pool->join();
g_pool.reset();
}