Если вы не хотите использовать async / await внутри вашего метода, но по-прежнему «украшаете» его, чтобы иметь возможность использовать ключевое слово await извне, 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
Если целью является улучшение масштабируемости (а не отзывчивости), все это зависит от наличия внешнего ввода / вывода, который предоставляет возможность сделать это.