Skip to content

ManagedTask Documentation (Russian)

Maxim Rakov edited this page Jun 11, 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)
  • $callable: callable - PHP callable для выполнения в качестве задачи.

Start

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

Pause

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

Resume

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

Stop

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

Wait

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

GetResultSync

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

ContinueWith

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

Dispose

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

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

Пример 1: Базовое выполнение задачи

<?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();

Пример 2: Обработка ошибок

<?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()}");
}

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

<?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();

Пример 4: Задача-продолжение

<?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();

Пример 5: Обработка ошибок через 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();

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

<?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();

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

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

<?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();

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

Пример 10: Обновление 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++;
            sleep(1);
            $autoResetEvent->Set();
        }
        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(): Освобождает доступ к критической секции, позволяя другим потокам войти в неё.
Clone this wiki locally