Skip to content

ManagedTask Documentation (Russian)

Maxim Rakov edited this page Jun 12, 2024 · 13 revisions

Класс: ManagedTask

ManagedTask - это класс, который предоставляет возможность выполнять и управлять асинхронными задачами в PHP. Он поддерживает продолжение задач, паузу, возобновление и отмену. Эта документация объясняет, как использовать ManagedTask в ваших PHP приложениях, с подробными объяснениями и примерами.

Свойства

  • Id: int - Получает уникальный идентификатор задачи.
  • Status: TaskStatus - Получает текущий статус задачи.
  • IsCompleted: bool - Указывает, завершена ли задача.
  • IsCanceled: bool - Указывает, была ли задача отменена.
  • IsCompletedSuccessfully: bool - Указывает, завершена ли задача успешно.
  • IsFaulted: bool - Указывает, произошла ли ошибка в задаче.
  • IsPaused: bool - Указывает, приостановлена ли задача.
  • IsStarted: bool - Указывает, запущена ли задача.

События

  • Completed: event(EventHandler) - Возникает, когда задача завершена.

Методы

Конструктор

//Инициализирует новый экземпляр класса ManagedTask.
public function __construct(callable $callable, ManagedTaskCreationOptions $options = ManagedTaskCreationOptions::None)
  • $callable: callable - PHP callable для выполнения в качестве задачи.
  • $options: ManagedTaskCreationOptions - Опции для создания задачи.

Start

//Запускает задачу.
public function Start();

Run

// Создает и запускает новую задачу.
public static function Run(callable $callable, ManagedTaskCreationOptions $options = ManagedTaskCreationOptions::None): ManagedTask
  • $callable: callable - PHP callable для выполнения в качестве задачи.
  • $options: ManagedTaskCreationOptions - Опции для создания задачи.

Pause

//Приостанавливает задачу.
public function Pause();

Resume

//Возобновляет задачу, если она была приостановлена.
public function Resume();

Stop

//Останавливает задачу.
public function Stop();

Wait

Ожидает завершения задачи.
public function Wait();

WaitAll

// Ожидает завершения всех переданных задач.
public static function WaitAll(array $managedTasks): void
  • $managedTasks: array - PHP массив объектов ManagedTask.\

WaitAny

// Ожидает завершения любой из переданных задач.
public static function WaitAny(array $managedTasks): int
  • $managedTasks: array - PHP массив объектов ManagedTask.
  • Возвращает индекс первой завершившейся задачи.

GetResultSync

//Синхронно получает результат задачи.
public function GetResultSync();

ContinueWith

//Создает задачу-продолжение, которая выполняется при завершении текущей задачи.
public function ContinueWith(callable $continuationAction, ManagedTaskCreationOptions $options = ManagedTaskCreationOptions::None): ManagedTask
  • $continuationAction: callable - Действие, которое нужно выполнить при завершении задачи.
  • $options: ManagedTaskCreationOptions - Опции для создания задачи-продолжения.

Dispose

//Освобождает задачу и её ресурсы.
public function Dispose();

Примеры использования

Базовое выполнение задачи

<?php
use Php\Threading\Tasks\ManagedTask;
use Php\Threading\Tasks\ManagedTaskEventArgs;
use Php\Output\Logger;

function simpleTask() {
    // Симуляция работы
    sleep(2);
    return "Задача выполнена";
}

$managedTask = new ManagedTask('simpleTask');

$managedTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
    Logger::Info("Задача {$e->TaskId} завершена с результатом: {$e->Result}");
});

$managedTask->Start();
$managedTask->Wait();

Объединение нескольких задач с использованием Run и ManagedTaskCreationOptions

В этом примере мы демонстрируем, как можно использовать метод Run для создания и запуска задач последовательно:

  1. Первая задача fetchData симулирует получение данных и запускается с помощью ManagedTask::Run.
  2. После завершения первой задачи запускается вторая задача processData, которая обрабатывает полученные данные и имеет опцию ManagedTaskCreationOptions::AttachedToParent, указывающую, что задача является дочерней по отношению к родительской.
  3. После завершения второй задачи запускается третья задача saveData, которая сохраняет обработанные данные и также имеет опцию ManagedTaskCreationOptions::AttachedToParent.
  4. Все задачи связаны между собой с использованием событий Completed, чтобы каждая последующая задача запускалась при завершении предыдущей.
<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Threading\Tasks\ManagedTaskCreationOptions;
use Php\Threading\Tasks\ManagedTaskEventArgs;
use Php\Output\Logger;

function fetchData(): string {
    sleep(2); // Симуляция задержки запроса
    return "Данные получены";
}

function processData(string $data): string {
    sleep(1); // Симуляция обработки данных
    return "Обработанные данные: $data";
}

function saveData(string $data): string {
    sleep(1); // Симуляция сохранения данных
    return "Данные сохранены: $data";
}

// Создаем и запускаем задачу получения данных
$fetchTask = ManagedTask::Run('fetchData');

$fetchTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
    $data = $e->Result;
    Logger::Info("Получены данные: $data");

    // Создаем и запускаем задачу обработки данных при завершении первой задачи
    $processTask = ManagedTask::Run(function() use ($data) {
        return processData($data);
    } , ManagedTaskCreationOptions::AttachedToParent);

    $processTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
        $processedData = $e->Result;
        Logger::Info("Обработаны данные: $processedData");

        // Создаем и запускаем задачу сохранения данных при завершении второй задачи
        $saveTask = ManagedTask::Run(function() use ($processedData) {
            return saveData($processedData);
        } , ManagedTaskCreationOptions::AttachedToParent);

        $saveTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
            Logger::Info($e->Result);
        });
    });
});

$fetchTask->Wait();

Ожидание завершения всех задач с использованием WaitAll

В этом примере мы демонстрируем, как можно использовать метод WaitAll для ожидания завершения всех задач в массиве:

  1. Создаем три задачи: task1, task2 и task3, каждая из которых выполняет свою работу с разной задержкой.
  2. Помещаем созданные задачи в массив $tasks.
  3. Используем метод ManagedTask::WaitAll для ожидания завершения всех задач в массиве.
  4. После завершения всех задач выводится сообщение "Все задачи завершены".
<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Output\Logger;

function task1(): string {
    sleep(2);
    return "Задача 1 выполнена";
}

function task2(): string {
    sleep(3);
    return "Задача 2 выполнена";
}

function task3(): string {
    sleep(1);
    return "Задача 3 выполнена";
}

$task1 = ManagedTask::Run('task1');
$task2 = ManagedTask::Run('task2');
$task3 = ManagedTask::Run('task3');

$tasks = [$task1, $task2, $task3];

// Ожидаем завершения всех задач
ManagedTask::WaitAll($tasks);

Logger::Info("Все задачи завершены");

Ожидание завершения любой задачи с использованием WaitAny

<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Threading\Tasks\ManagedTaskCreationOptions;
use Php\Output\Logger;

function task1(): string {
    sleep(2);
    return "Задача 1 выполнена";
}

function task2(): string {
    sleep(3);
    return "Задача 2 выполнена";
}

function task3(): string {
    sleep(1);
    return "Задача 3 выполнена";
}

$task1 = ManagedTask::Run('task1');
$task2 = ManagedTask::Run('task2');
$task3 = ManagedTask::Run('task3');

$tasks = [$task1, $task2, $task3];

// Ожидаем завершения любой задачи
$completedTaskIndex = ManagedTask::WaitAny($tasks);

Logger::Info("Задача с индексом {$completedTaskIndex} завершена первой");

Обработка ошибок

<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Threading\Tasks\ManagedTaskEventArgs;
use Php\Threading\Tasks\ManagedTaskException;
use Php\Output\Logger;

/**
 * @throws Exception
 */
function taskWithError() {
    throw new Exception("Что-то пошло не так!");
}

$managedTask = new ManagedTask('taskWithError');

$managedTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
    if ($e->Result instanceof ManagedTaskException) {
        Logger::Error("Ошибка задачи: {$e->Result->getMessage()}");
    } else {
        Logger::Info("Задача {$e->TaskId} завершена с результатом: {$e->Result}");
    }
});

try {
    $managedTask->Start();
    $managedTask->Wait();
} catch (ManagedTaskException $ex) {
    Logger::Error("Пойманная ошибка задачи: {$ex->getMessage()}");
}

Приостановка и возобновление задачи

<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Output\Logger;
use System\Threading\AutoResetEvent;
use System\Threading\CancellationToken;

function longRunningTask(CancellationToken $cancellationToken, AutoResetEvent $autoResetEvent): string
{
    for ($i = 0; $i < 10; $i++) {
        if ($cancellationToken->IsCancellationRequested) {
            return "Задача отменена";
        }

        $autoResetEvent->WaitOne(); // Механизм паузы
        Logger::Info("Этап задачи: $i");
        $autoResetEvent->Set();
        sleep(1); // Симуляция работы
    }
    return "Задача выполнена";
}

$managedTask = new ManagedTask('longRunningTask');

$managedTask->Start();
sleep(3); // Дать задаче немного поработать

$managedTask->Pause();
Logger::Info("Задача приостановлена");
sleep(3); // Пауза

$managedTask->Resume();
Logger::Info("Задача возобновлена");

$managedTask->Wait();

Задача-продолжение

<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Threading\Tasks\ManagedTaskEventArgs;
use Php\Output\Logger;
use System\Threading\AutoResetEvent;
use System\Threading\CancellationToken;

function initialTask(): string
{
    return "Результат начальной задачи";
}

function continuationTask(CancellationToken $cancellationToken, AutoResetEvent $autoResetEvent, mixed $previousResult): string
{
    return "Задача-продолжение с предыдущим результатом: $previousResult";
}

$managedTask = new ManagedTask('initialTask');
$continuedTask = $managedTask->ContinueWith('continuationTask');

$managedTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
    Logger::Info("Начальная задача завершена с результатом: {$e->Result}");
});

$continuedTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
    Logger::Info("Задача-продолжение завершена с результатом: {$e->Result}");
});

$managedTask->Start();
$managedTask->Wait();

Обработка ошибок через try catch

<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Threading\Tasks\ManagedTaskEventArgs;
use Php\Threading\Tasks\ManagedTaskException;
use Php\Output\Logger;
use System\Threading\AutoResetEvent;
use System\Threading\CancellationToken;

function taskWithCatch(CancellationToken $cancellationToken, AutoResetEvent $autoResetEvent)
{
    try {
        // Симуляция работы
        $autoResetEvent->WaitOne();
        // Здесь можно вызвать ошибку
        throw new Exception("Ошибка внутри задачи");
        $autoResetEvent->Set();
        return "Задача выполнена";
    } catch (Exception $ex) {
        $autoResetEvent->Set();
        return new ManagedTaskException($ex->getMessage());
    }
}

$managedTask = new ManagedTask('taskWithCatch');

$managedTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
    if ($e->Result instanceof ManagedTaskException) {
        Logger::Error("Ошибка задачи: {$e->Result->getMessage()}");
    } else {
        Logger::Info("Задача {$e->TaskId} завершена с результатом: {$e->Result}");
    }
});

$managedTask->Start();
$managedTask->Wait();

Синхронизация задач с помощью Mutex

В этом примере мы используем Mutex для синхронизации доступа к критической секции кода. WaitOne на mutex блокирует текущий поток до тех пор, пока не будет получен доступ к критической секции, а ReleaseMutex освобождает этот доступ.

<?php

<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Output\Logger;
use System\Threading\Mutex;
use System\Threading\AutoResetEvent;
use System\Threading\CancellationToken;

class SyncTaskExample {
    private Mutex $mutex;

    public function __construct() {
        $this->mutex = new Mutex();
    }

    public function taskWithSync(CancellationToken $cancellationToken, AutoResetEvent $autoResetEvent): string
    {
        for ($i = 0; $i <= 10; $i++) {
            if ($cancellationToken->IsCancellationRequested) {
                return "Задача отменена";
            }

            $autoResetEvent->WaitOne();
            $this->mutex->WaitOne(); // Синхронизация задачи
            try {
                Logger::Info("Этап задачи: $i");
            } finally {
                $this->mutex->ReleaseMutex();
            }
            $autoResetEvent->Set();

            sleep(1); // Симуляция работы
        }
        return "Задача выполнена";
    }
}

$example = new SyncTaskExample();
$managedTask = new ManagedTask([$example, 'taskWithSync']);

$managedTask->Start();
$managedTask->Wait();

Скачивание изображения из интернета и сохранение рядом с программой с помощью HttpClient и ManagedTask

В этом примере мы используем HttpClient для скачивания изображения из интернета и сохранения его на диск. GetAsync выполняет асинхронный запрос, а ReadAsStreamAsync считывает содержимое ответа как поток. Затем мы сохраняем этот поток в файл logo.png.

<?php

use Php\Threading\Tasks\ManagedTask;
use Php\Output\Logger;
use Php\Threading\Tasks\ManagedTaskEventArgs;
use Php\Threading\Tasks\ManagedTaskException;
use System\Net\Http\HttpClient;
use System\Net\Http\HttpRequestException;
use System\IO\FileStream;
use System\IO\FileMode;
use System\IO\FileAccess;

/**
 * @throws ManagedTaskException
 */
function downloadImageTask($url): string
{
    $httpClient = new HttpClient();

    try {
        $response = $httpClient->GetAsync($url)->Result;
        $response->EnsureSuccessStatusCode();
        $stream = $response->Content->ReadAsStreamAsync()->Result;
        $filePath = __DIR__ . '/logo.png';
        $fileStream = new FileStream($filePath, FileMode::Create, FileAccess::Write);
        $stream->CopyTo($fileStream);
    } catch (HttpRequestException $httpRequestException) {
        return "Не удалось загрузить файл: " . $httpRequestException->Message;
    } catch(Exception $exception) {
        return "Ошибка в методе Load: " . $exception->getMessage();
    } finally {
        $httpClient->Dispose();
    }

    return "Изображение скачано и сохранено как logo.png";
}

$managedTask = new ManagedTask(function (){
    return downloadImageTask("https://github.com/FibonacciFox/Peachpie.Avalonia/blob/master/Samples/Application/Assets/Logo.png?raw=true");
});

$managedTask->Completed->add(function(ManagedTask $sender, ManagedTaskEventArgs $e) {
    Logger::Info($e->Result);
});

$managedTask->Start();
$managedTask->Wait();
$managedTask->Dispose();

Обновление UI из потока диспетчера Avalonia UI

<?php

use Php\Threading\Tasks\ManagedTask;
use Avalonia\Threading\Dispatcher;
use System\Threading\AutoResetEvent;
use System\Threading\CancellationToken;

class AvaloniaUIExample {
    private $textblock;

    public function __construct($textblock) {
        $this->textblock = $textblock;
    }

    public function updateUITask(CancellationToken $cancellationToken, AutoResetEvent $autoResetEvent): string
    {
        $counter = 0;
        while (!$cancellationToken->IsCancellationRequested) {
            $autoResetEvent->WaitOne();
            Dispatcher::$UIThread->Post(function() use ($counter) {
                $this->textblock->Text = "Счетчик: $counter";
            });
            $counter++;
            $autoResetEvent->Set();
            sleep(1);
        }
        return "Обновление UI завершено";
    }
}

// Предположим, что $textblock это элемент Avalonia UI, который мы хотим обновлять
$textblock = ...;

$example = new AvaloniaUIExample($textblock);
$managedTask = new ManagedTask([$example, 'updateUITask']);

$managedTask->Start();
sleep(10); // Позволяем задаче обновлять UI в течение 10 секунд

$managedTask->Stop();
$managedTask->Wait();

В этом примере мы используем Dispatcher для обновления элемента UI из асинхронной задачи. Dispatcher::$UIThread->Post позволяет выполнить код в потоке UI, обеспечивая безопасное обновление элементов интерфейса.

Дополнительно

AutoResetEvent

  • $autoResetEvent->WaitOne(): Ожидает сигнал, что позволяет приостановить выполнение задачи до тех пор, пока не будет получен сигнал для продолжения.
  • $autoResetEvent->Set(): Устанавливает сигнал, разрешая продолжение выполнения задачи.

CancellationToken

  • $cancellationToken->IsCancellationRequested: Проверяет, была ли запрошена отмена задачи. Если да, выполнение задачи должно быть прервано.

Mutex

  • $this->mutex->WaitOne(): Блокирует текущий поток до тех пор, пока не будет получен доступ к критической секции.
  • $this->mutex->ReleaseMutex(): Освобождает доступ к критической секции, позволяя другим потокам войти в неё.