LINUX.ORG.RU

История изменений

Исправление 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();
}