Якщо ви не хочете використовувати async / чакати всередині свого методу, але все-таки "прикрасьте" його так, щоб мати можливість використовувати ключове слово очікування ззовні, TaskCompletionSource.cs :
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
Звідси і звідси
Щоб підтримати таку парадигму із Завданнями, нам потрібен спосіб зберегти фасад завдання і можливість посилатися на довільну асинхронну операцію як на Задачу, але контролювати термін експлуатації цієї задачі відповідно до правил базової інфраструктури, що забезпечує асинхронність, і це робити таким чином, що не коштує суттєво. Це мета TaskCompletionSource.
Я бачив, що використовується також у джерелі .NET, наприклад. WebClient.cs :
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<string>(address);
// Setup the callback event handler
UploadStringCompletedEventHandler handler = null;
handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
this.UploadStringCompleted += handler;
// Start the async operation.
try { this.UploadStringAsync(address, method, data, tcs); }
catch
{
this.UploadStringCompleted -= handler;
throw;
}
// Return the task that represents the async operation
return tcs.Task;
}
Нарешті, я виявив корисним також таке:
Мені постійно задають це питання. Мається на увазі, що десь повинен бути якийсь потік, який блокує виклик вводу / виводу до зовнішнього ресурсу. Отже, асинхронний код звільняє потік запиту, але лише за рахунок іншого потоку в іншому місці системи, правда? Ні, зовсім ні. Щоб зрозуміти, чому шкала асинхронних запитів, я простежу (спрощений) приклад асинхронного виклику вводу / виводу. Скажімо, запит потрібно написати у файл. Нитка запиту викликає метод асинхронного запису. WriteAsync реалізований бібліотекою базових класів (BCL) і використовує порти завершення для свого асинхронного вводу / виводу. Отже, виклик WriteAsync передається в ОС як асинхронний запис файлу. Потім ОС спілкується зі стеком драйверів, передаючи дані для запису в пакет запиту вводу-виводу (IRP). Тут цікаві речі: Якщо драйвер пристрою не може відразу обробити IRP, він повинен обробляти його асинхронно. Отже, драйвер каже диску, щоб він почав писати, і повертає "очікуваний" відповідь в ОС. ОС передає цей "очікуваний" відповідь на BCL, і BCL повертає незавершене завдання до коду обробки запитів. Код обробки запитів очікує завдання, яке повертає незавершене завдання з цього методу тощо. Нарешті, код обробки запитів закінчується поверненням незавершеного завдання ASP.NET, і потік запиту звільняється для повернення до пулу потоків. Код обробки запитів очікує завдання, яке повертає незавершене завдання з цього методу тощо. Нарешті, код обробки запитів закінчується поверненням незавершеного завдання ASP.NET, і потік запиту звільняється для повернення до пулу потоків. Код обробки запитів очікує завдання, яке повертає незавершене завдання з цього методу тощо. Нарешті, код обробки запитів закінчується поверненням незавершеного завдання ASP.NET, і потік запиту звільняється для повернення до пулу потоків.
Вступ до Async / Await на ASP.NET
Якщо мета полягає в покращенні масштабованості (а не чутливості), то все покладається на існування зовнішнього вводу-виводу, який надає можливість це зробити.