TaskCompletionSource<T>

  • Михаил
  • 8 мин. на прочтение
  • 44
  • 15 Nov 2024
  • 15 Nov 2024

TaskCompletionSource<T> (TCS) — это мощный инструмент в .NET, который позволяет вам создавать и управлять задачами (Task<T>) вручную. Он предоставляет методы для завершения задачи с результатом, исключением или отменой.

Давайте рассмотрим простой пример, чтобы понять, как работает TaskCompletionSource<T>.

Пример: Создание задачи с использованием TaskCompletionSource<T>
Предположим, у нас есть метод, который асинхронно вычисляет квадрат числа. Мы хотим использовать TaskCompletionSource<int> для управления завершением этой задачи.

using System;
using System.Threading.Tasks;
public class Program
{
   public static async Task Main(string[] args)
   {
       var tcs = new TaskCompletionSource<int>();
       // Запускаем асинхронную операцию
       CalculateSquare(5, tcs);
       // Ожидаем завершения задачи
       int result = await tcs.Task;
       Console.WriteLine($"Result: {result}");
   }
   public static void CalculateSquare(int number, TaskCompletionSource<int> tcs)
   {
       // Имитация асинхронной операции
       Task.Run(() =>
       {
           int square = number * number;
           tcs.SetResult(square); // Завершаем задачу с результатом
       });
   }
}

Объяснение

Создание TaskCompletionSource<int>:

var tcs = new TaskCompletionSource<int>();

Мы создаем экземпляр TaskCompletionSource<int>, который будет управлять задачей.

Запуск асинхронной операции:

CalculateSquare(5, tcs);

Мы вызываем метод CalculateSquare, который принимает число и TaskCompletionSource<int>.

Асинхронная операция:

public static void CalculateSquare(int number, TaskCompletionSource<int> tcs)
{
   Task.Run(() =>
   {
       int square = number * number;
       tcs.SetResult(square); // Завершаем задачу с результатом
   });
}

Внутри метода CalculateSquare мы запускаем асинхронную операцию с помощью Task.Run. После вычисления квадрата числа мы вызываем tcs.SetResult(square), чтобы завершить задачу с результатом.

Ожидание завершения задачи:

int result = await tcs.Task;

В методе Main мы ожидаем завершения задачи с помощью await tcs.Task. Когда задача завершится, мы получим результат и выведем его на консоль.

Основные методы TaskCompletionSource<T>

  • SetResult(T result): Завершает задачу с указанным результатом.
  • SetException(Exception exception): Завершает задачу с указанным исключением.
  • SetCanceled(): Завершает задачу как отмененную.
  • TrySetResult(T result): Попытка завершить задачу с указанным результатом. Возвращает true, если задача была успешно завершена, и false, если задача уже была завершена.
  • TrySetException(Exception exception): Попытка завершить задачу с указанным исключением.
  • TrySetCanceled(): Попытка завершить задачу как отмененную.


Пример с таймаутом
Теперь давайте рассмотрим пример с таймаутом, чтобы понять, как можно использовать TaskCompletionSource<T> для управления завершением задачи с учетом времени ожидания.

using System;
using System.Threading.Tasks;
public class Program
{
   public static async Task Main(string[] args)
   {
       var tcs = new TaskCompletionSource<int>();
       // Запускаем асинхронную операцию
       CalculateSquareWithDelay(5, tcs);
       // Запускаем таймаут
       var timeoutTask = Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => tcs.TrySetCanceled());
       // Ожидаем завершения одной из задач
       var completedTask = await Task.WhenAny(tcs.Task, timeoutTask);
       if (completedTask == timeoutTask)
       {
           Console.WriteLine("Operation timed out.");
       }
       else
       {
           int result = await tcs.Task;
           Console.WriteLine($"Result: {result}");
       }
   }
   public static void CalculateSquareWithDelay(int number, TaskCompletionSource<int> tcs)
   {
       Task.Run(async () =>
       {
           await Task.Delay(500); // Имитация задержки
           int square = number * number;
           tcs.SetResult(square); // Завершаем задачу с результатом
       });
   }
}

Объяснение

Создание TaskCompletionSource<int>:

var tcs = new TaskCompletionSource<int>();

Запуск асинхронной операции:

CalculateSquareWithDelay(5, tcs);

Запуск таймаута:

var timeoutTask = Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => tcs.TrySetCanceled());

Мы создаем задачу таймаута, которая завершит TaskCompletionSource<int> как отмененную, если операция не завершится в течение 1 секунды.

Ожидание завершения одной из задач:

var completedTask = await Task.WhenAny(tcs.Task, timeoutTask);

Мы ожидаем завершения одной из задач (асинхронной операции или таймаута) с помощью Task.WhenAny.

Проверка завершенной задачи:

if (completedTask == timeoutTask)
{
   Console.WriteLine("Operation timed out.");
}
else
{
   int result = await tcs.Task;
   Console.WriteLine($"Result: {result}");
}

Если завершилась задача таймаута, выводим сообщение о таймауте. В противном случае получаем результат и выводим его на консоль.

Этот пример демонстрирует, как можно использовать TaskCompletionSource<T> для управления завершением задачи с учетом времени ожидания.