О, это звучит интересно. Я еще не играю с CTP, просто просматривая документ. Увидев Anders Hejlsberg's talk, я думаю, что могу видеть, как это может оказаться полезным.
Как я понимаю, async упрощает чтение и реализацию асинхронных вызовов. Точно так же писать итераторы проще сейчас (в отличие от написания функций вручную). Это важные блокирующие процессы, так как никакая полезная работа не может быть выполнена, пока она не будет разблокирована. Если вы загружаете файл, вы не можете сделать ничего полезного, пока не получите этот файл, чтобы поток стал пустым. Подумайте, как можно назвать функцию, которая, как вы знаете, блокирует неопределенную длину и возвращает некоторый результат, затем обрабатывает ее (например, сохраняет результаты в файле). Как бы вы это написали? Вот простой пример:
static object DoSomeBlockingOperation(object args)
{
// block for 5 minutes
Thread.Sleep(5 * 60 * 1000);
return args;
}
static void ProcessTheResult(object result)
{
Console.WriteLine(result);
}
static void CalculateAndProcess(object args)
{
// let's calculate! (synchronously)
object result = DoSomeBlockingOperation(args);
// let's process!
ProcessTheResult(result);
}
Хорошо, мы его реализовали. Но подождите, для расчета потребуется несколько минут. Что делать, если мы хотели иметь интерактивное приложение и делать другие вещи во время вычисления (например, для создания пользовательского интерфейса)? Это нехорошо, так как мы вызываем функцию синхронно, и нам нужно дождаться ее, чтобы закончить эффективное замораживание приложения, так как поток ожидает разблокировки.
Ответ, вызовите функцию дорогостоящей функции асинхронно. Таким образом, мы не обязаны ждать завершения операции блокировки. Но как мы это делаем? Мы будем вызывать функцию асинхронно и регистрировать функцию обратного вызова, которая будет вызываться при разблокировке, чтобы мы могли обработать результат.
static void CalculateAndProcessAsyncOld(object args)
{
// obtain a delegate to call asynchronously
Func<object, object> calculate = DoSomeBlockingOperation;
// define the callback when the call completes so we can process afterwards
AsyncCallback cb = ar =>
{
Func<object, object> calc = (Func<object, object>)ar.AsyncState;
object result = calc.EndInvoke(ar);
// let's process!
ProcessTheResult(result);
};
// let's calculate! (asynchronously)
calculate.BeginInvoke(args, cb, calculate);
}
- Примечание: Конечно, мы могли бы начать другую нить, чтобы сделать это, но это означало бы, что мы порождая поток, который просто сидит там ждет, чтобы быть разблокированы, а затем сделать некоторую полезную работу. Это будет пустой тратой.
Теперь вызов асинхронный, и нам не нужно беспокоиться о ожидании завершения и обработки вычисления, это выполняется асинхронно. Он закончит, когда сможет. Альтернатива вызова кода асинхронно напрямую, вы можете использовать задачу:
static void CalculateAndProcessAsyncTask(object args)
{
// create a task
Task<object> task = new Task<object>(DoSomeBlockingOperation, args);
// define the callback when the call completes so we can process afterwards
task.ContinueWith(t =>
{
// let's process!
ProcessTheResult(t.Result);
});
// let's calculate! (asynchronously)
task.Start();
}
Теперь мы назвали нашу функцию асинхронно. Но что нужно, чтобы добиться этого? Прежде всего, нам нужно, чтобы делегат/задача, чтобы иметь возможность называть его асинхронно, нам нужна функция обратного вызова, чтобы иметь возможность обрабатывать результаты, а затем вызывать функцию. Мы перешли на вызов с двумя линиями, чтобы гораздо больше просто назвать что-то асинхронно. Мало того, логика в коде стала более сложной, чем это было или могло быть. Хотя использование задачи помогло упростить процесс, нам все равно нужно было сделать что-то, чтобы это произошло. Мы просто хотим запустить асинхронно, а затем обработать результат. Почему мы не можем это сделать?Хорошо теперь мы можем:
// need to have an asynchronous version
static async Task<object> DoSomeBlockingOperationAsync(object args)
{
//it is my understanding that async will take this method and convert it to a task automatically
return DoSomeBlockingOperation(args);
}
static async void CalculateAndProcessAsyncNew(object args)
{
// let's calculate! (asynchronously)
object result = await DoSomeBlockingOperationAsync(args);
// let's process!
ProcessTheResult(result);
}
Теперь это был очень упрощенный пример с простыми операциями (вычисление, обработка). Представьте себе, что каждая операция не может быть удобно помещена в отдельную функцию, но вместо этого есть сотни строк кода. Это сложная сложность, чтобы получить выгоду от асинхронного вызова.
Другой практический пример, используемый в технической документации, используется в приложениях пользовательского интерфейса. Модифицированный использовать приведенный выше пример:
private async void doCalculation_Click(object sender, RoutedEventArgs e) {
doCalculation.IsEnabled = false;
await DoSomeBlockingOperationAsync(GetArgs());
doCalculation.IsEnabled = true;
}
Если вы сделали любое программирование пользовательского интерфейса (будь то WinForms или WPF) и попытался вызвать дорогостоящую функцию внутри обработчика, вы будете знать, что это удобно. Использование фона рабочего для этого было бы не так полезно, поскольку фоновый поток будет сидеть там, пока он не сможет работать.
Предположим, что у вас был способ управлять каким-либо внешним устройством, допустим, принтером. И вы хотели перезапустить устройство после сбоя. Естественно, потребуется некоторое время для запуска принтера и готовности к работе. Возможно, вам придется учитывать перезагрузку, не помогая и снова пытаться перезапустить. У вас нет выбора, кроме как ждать его. Нет, если вы сделали это асинхронно.
static async void RestartPrinter()
{
Printer printer = GetPrinter();
do
{
printer.Restart();
printer = await printer.WaitUntilReadyAsync();
} while (printer.HasFailed);
}
Представьте себе, что пишут без асинхронного цикла.
Последний пример у меня есть. Представьте себе, что вам нужно было выполнить несколько операций блокировки в функции и вы хотите вызвать асинхронно. Что бы вы предпочли?
static void DoOperationsAsyncOld()
{
Task op1 = new Task(DoOperation1Async);
op1.ContinueWith(t1 =>
{
Task op2 = new Task(DoOperation2Async);
op2.ContinueWith(t2 =>
{
Task op3 = new Task(DoOperation3Async);
op3.ContinueWith(t3 =>
{
DoQuickOperation();
}
op3.Start();
}
op2.Start();
}
op1.Start();
}
static async void DoOperationsAsyncNew()
{
await DoOperation1Async();
await DoOperation2Async();
await DoOperation3Async();
DoQuickOperation();
}
Читать whitepaper, он на самом деле имеет много практических примеров, как писать параллельные задачи и другие.
Я не могу дождаться, когда начну играть с этим либо в CTP, либо когда .NET 5.0, наконец, это выйдет.
Или ставить вопрос в более общем плане, когда асинхронно полезно? Почти любой общий пример асинхронного анализа должен иметь отношение к тому, что было показано на C# 5. – Larsenal
Когда вы говорите «не-сетевые», вы действительно имеете в виду «не-I/O»? Потому что действительно все I/O могут блокироваться. – Gabe
+1 Предположим, что я делясь потоками данных из сети, но через трехсторонний терминал, и я не знаю, что находится внутри терминала или как он получает данные из сети, я просто использую его dll и имею все данные в моей программе, появляющейся асинхронно как магия. Не нужно ничего знать о сети. Но мне нужно знать async/await –