TaskCompletionSource<T> решает одну конкретную проблему: вы хотите вернуть Task, но не можете использовать async/await, потому что результат приходит через коллбэк, событие или другой внешний сигнал.Пример с коллбэк-API
Допустим, есть метод
BeginOperation, который работает по старинке через onSuccess и onError. Оборачиваем его в нормальный Task:public Task<string> GetDataAsync()
{
var tcs = new TaskCompletionSource<string>(
TaskCreationOptions.RunContinuationsAsynchronously);
BeginOperation(
onSuccess: result => tcs.SetResult(result),
onError: ex => tcs.SetException(ex));
return tcs.Task;
}
Теперь вызывающий код просто пишет
await GetDataAsync() и не знает, что внутри коллбэки.Почему важен
RunContinuationsAsynchronouslyЭто не просто флаг для галочки. Без него продолжения выполняются прямо на том потоке, который вызвал
SetResult. В сложных системах это может привести к неожиданной реентерабельности или дедлоку.С флагом продолжения уходят в пул потоков и поведение становится предсказуемым.
Координация сигналов
Настоящая сила
TaskCompletionSource проявляется, когда нужно синхронизировать несколько частей системы:public Task WaitForSignalAsync()
{
return _signalTcs.Task;
}
public void Signal()
{
_signalTcs.TrySetResult();
}
Один компонент ждёт сигнала через
await WaitForSignalAsync(), другой вызывает Signal() когда готов. Это базовый паттерн для кастомных async-локов, очередей и event-систем.Когда использовать
Подходит, если нужно обернуть коллбэк-API в
Task, реализовать собственный примитив синхронизации, или управлять завершением задачи вручную из внешнего кода. Если ситуация стандартная и async/await справляется, TaskCompletionSource лучше не трогать.📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
