Task- это какой-то кусок вычислений. Сам код вычисления находится в методеRun()и определяется пользователем.Executor- это набор потоков, которые могут выполнятьTask-и.Executorдолжен запускать потоки в конструкторе, во время работы новых потоков создаваться не должно.- Чтобы начать выполнять
Task, пользователь должен отправить его вExecutorс помощью методаSubmit(). - После этого, пользователь может дождаться пока
Taskзавершится, позвав методTask::Wait.
class MyPrimeSplittingTask : public Task {
Params params_;
public:
MyPrimeSplittingTask(Params params) : params_{params} {
}
bool is_prime = false;
void Run() override {
is_prime = CheckIsPrime(params_);
}
}
bool DoComputation(std::shared_ptr<Executor> pool, Params params) {
auto my_task = std::make_shared<MyPrimeSplittingTask>(params);
pool->Submit(my_task);
my_task->Wait();
return my_task->is_prime;
}-
Taskможет завершиться успешно (IsCompleted), с ошибкой (IsFailed) и быть отменён (IsCanceled). После того, как с ним произошло одно из этих событий - он считается выполненным (IsFinished). -
Пользователь может в любой момент отменить
Taskс помощью методаCancel(). В этом случае, если выполнениеTask-и еще не началось, то оно и не должно начаться. -
Taskможет иметь зависимости. Например в задаче reduce сначала должны были выполниться reduce-ы по кускам вектора, а потом один финальный reduce по промежуточным значениям. Пользователь может сказать, что одинTaskдолжен выполняться только после того как выполнился какой-то другойTask, позвав методTask::AddDependency. -
Taskможет иметь триггеры (Task::AddTrigger). В таком случае он должен начать выполнение после того как хотя бы один триггер завершился. -
Taskможет иметь один триггер по времени (Task::SetTimeTrigger). В этом случае он должен начать выполнение если наступило времяdeadline. -
В общем случае,
Executor::Submitне должен начинать выполнение сразу, а дожидаться условия:- Или есть зависомости и все они выполнились
- Или один из триггеров выполнился
- Или выставлен
deadline, и наступило времяdeadline. Если у таска нет зависимостей, нет триггеров и не выставлен deadline, то его можно выполнять сразу же. ПокаSubmitна таск не вызван, выполнять его нельзя.
-
Executorпредоставляет API для того, чтобы остановить выполнение.Executor::StartShutdown- начинает процесс остановки.Task-и, которые были посланы послеStartShutdown, должны сразу переходить в состояние Canceled. Функция может быть вызвана несколько раз.Executor::WaitShutdown- блокируется, пока Executor не остановится. Функция может быть вызвана несколько раз.Executor::~Executor- неявно делает shutdown и дожидается завершения потоков.
Интерфейсы Task и Executor являются довольно многословными, во второй
части задания вам нужно будет реализовать класс Future и несколько комбинаторов к нему.
-
Future- этоTask, у которого есть результат (какое-то значение). -
Интерфейсы комбинаторов определены в классе
Executor:Invoke(fn)- выполнитьfnвнутриExecutor-а, результат вернуть черезFuture.Then(input, fn)- выполнитьfn, после того как закончитсяinput. ВозвращаетFutureна результатfnне дожидаясь выполненияinput.WhenAll(vector<FuturePtr<T>>) -> FuturePtr<vector<T>>- собирает результат несколькихFutureв один.WhenFirst(vector<FuturePtr<T>>) -> FuturePtr<T>- возвращает результат, который появится первым.WhenAllBeforeDeadline(vector<FuturePtr<T>>, deadline) -> FuturePtr<vector<T>>- возвращает все результаты, которые успели появиться до deadline.