Как создать асинхронный метод в C#?
каждое сообщение в блоге, которое я читал, рассказывает вам, как использовать асинхронный метод в C#, но по какой-то странной причине никогда не объясняйте, как создавать свои собственные асинхронные методы для использования. Так что у меня есть этот код прямо сейчас, который потребляет мой метод:
private async void button1_Click(object sender, EventArgs e)
{
var now = await CountToAsync(1000);
label1.Text = now.ToString();
}
и я написал этот метод, который является CountToAsync
:
private Task<DateTime> CountToAsync(int num = 1000)
{
return Task.Factory.StartNew(() =>
{
for (int i = 0; i < num; i++)
{
Console.WriteLine("#{0}", i);
}
}).ContinueWith(x => DateTime.Now);
}
это использование Task.Factory
, лучший способ написать асинхронный метод, или я должен написать это по-другому?
2 ответа:
Не советую
StartNew
Если вам не нужен этот уровень сложности.если ваш асинхронный метод зависит от других асинхронных методов, самый простой подход-использовать
async
ключевые слова:private static async Task<DateTime> CountToAsync(int num = 10) { for (int i = 0; i < num; i++) { await Task.Delay(TimeSpan.FromSeconds(1)); } return DateTime.Now; }
если ваш асинхронный метод выполняет работу процессора, вы должны использовать
Task.Run
:private static async Task<DateTime> CountToAsync(int num = 10) { await Task.Run(() => ...); return DateTime.Now; }
вы можете найти меня
async
/await
интро полезная.
Если вы не хотите использовать 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, и поток запроса освобождается, чтобы вернуться в пул потоков.
введение в Async / Await on ASP.NET
Если цель состоит в том, чтобы улучшить масштабируемость (а не отзывчивость), все это зависит от существования внешнего ввода-вывода, который предоставляет возможность сделать это.