Может ли кто-нибудь объяснить, что делает функция await
?
Какая новая функция ожидания С#?
Ответ 1
Они просто вчера говорили об этом в PDC
Ожидание используется в сочетании с задачами (параллельное программирование) в .NET. Это ключевое слово, которое вводится в следующей версии .NET. Это более или менее позволяет вам "приостановить" выполнение метода, ожидающего завершения выполнения задачи. Вот краткий пример:
//create and run a new task
Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation);
//run some other code immediately after this task is started and running
ShowLoaderControl();
StartStoryboard();
//this will actually "pause" the code execution until the task completes. It doesn't lock the thread, but rather waits for the result, similar to an async callback
// please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return
dataTask.Start();
DataTable table = await dataTask;
//Now we can perform operations on the Task result, as if we're executing code after the async operation completed
listBoxControl.DataContext = table;
StopStoryboard();
HideLoaderControl();
Ответ 2
В основном ключевые слова async
и await
позволяют указать, что выполнение метода должно останавливаться при всех использованиях await
, которые отмечают вызовы асинхронного метода, а затем возобновляются после завершения асинхронной операции. Это позволяет вам вызвать метод в главном потоке приложения и обрабатывать сложную работу асинхронно, без необходимости явно определять потоки и присоединяться или блокировать основной поток приложения.
Подумайте, что он несколько похож на оператор yield return
в методе, создающем IEnumerable. Когда время выполнения достигнет значения yield
, оно в основном сохранит текущее состояние метода и вернет значение или ссылку. В следующий раз, когда IEnumerator.MoveNext() вызывается в возвращаемом объекте (который создается внутри среды выполнения), старое состояние метода восстанавливается в стек, и выполнение продолжается со следующей строкой после yield return
, как если бы мы никогда не покидал метод. Без этого ключевого слова тип IEnumerator должен быть настроен для хранения состояния и обрабатывать запросы итерации с помощью методов, которые действительно могут стать ОЧЕНЬ сложными.
Аналогично, метод, отмеченный как async
, должен иметь как минимум один await
. На await
среда выполнения сохранит текущее состояние потока и стек вызовов, сделает асинхронный вызов и откидывается назад к циклу сообщений времени выполнения, чтобы обрабатывать следующее сообщение и поддерживать приложение реагировать. Когда асинхронная операция завершена, при следующей возможности планирования стек вызовов для выполнения операции async возвращается и продолжается, как если бы вызов был синхронным.
Итак, эти два новых ключевых слова в основном упрощают кодирование асинхронных процессов, подобно тому, как yield return
упрощает создание пользовательских счетчиков. Имея пару ключевых слов и небольшое знание фона, вы можете пропустить все запутанные и часто подверженные ошибкам детали традиционного асинхронного шаблона. Это будет INVALUABLE практически в любом графическом интерфейсе, основанном на событиях, например Winforms, WPF Silverlight.
Ответ 3
В настоящее время принятый ответ вводит в заблуждение.
await
ничего не останавливает.
Прежде всего, его можно использовать только в методах или lambdas, отмеченных как async
, и возвращать Task
или void
, если вам не нужен экземпляр Task
, запущенный в этом методе.
Вот иллюстрация:
internal class Program
{
private static void Main(string[] args)
{
var task = DoWork();
Console.WriteLine("Task status: " + task.Status);
Console.WriteLine("Waiting for ENTER");
Console.ReadLine();
}
private static async Task DoWork()
{
Console.WriteLine("Entered DoWork(). Sleeping 3");
// imitating time consuming code
// in a real-world app this should be inside task,
// so method returns fast
Thread.Sleep(3000);
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("async task iteration " + i);
// imitating time consuming code
Thread.Sleep(1000);
}
});
Console.WriteLine("Exiting DoWork()");
}
}
Вывод:
Введено DoWork(). Спящий 3
асинхронная задача итерации 0
Статус задачи: WaitingForActivation
Ожидание ENTER
асинхронная задача итерация 1
асинхронная задача итерация 2
асинхронная задача итерации 3
асинхронная задача итерация 4
асинхронная задача итерации 5
асинхронная задача итерации 6
асинхронная итерация задач 7
асинхронная задача итерации 8
асинхронная задача итерации 9
Выход из DoWork()
Ответ 4
Для тех, кто новичок в асинхронном программировании в .NET, здесь (полностью фальшивая) аналогия в сценарии, с которым вы можете быть знакомы - вызовы AJAX с использованием JavaScript/jQuery. Простой пост jQuery AJAX выглядит следующим образом:
$.post(url, values, function(data) {
// AJAX call completed, do something with returned data here
});
Причина, по которой мы обрабатываем результаты в функции обратного вызова, заключается в том, что мы не блокируем текущий поток, ожидая возврата вызова AJAX. Только тогда, когда ответ готов, будет активирован обратный вызов, освобождая текущий поток, чтобы делать другие вещи в среднем.
Теперь, если JavaScript поддерживает ключевое слово await
(это, конечно же, не (пока!)), вы можете достичь то же самое:
var data = await $.post(url, values);
// AJAX call completed, do something with returned data here
Это намного чище, но, похоже, мы вводили синхронный, блокирующий код. Но компилятор (фальшивый) JavaScript взял бы все после await
и подключил его к обратному вызову, поэтому во время выполнения второй пример будет вести себя так же, как первый.
Может показаться, что это не сэкономит вам много работы, но когда дело доходит до таких вещей, как обработка исключений и контексты синхронизации, компилятор действительно делает много тяжелой работы для вас. Более того, я бы рекомендовал часто задаваемые вопросы, а затем Серия блога Стивена Клири.
Ответ 5
Если бы мне пришлось реализовать его в Java, это выглядело бы примерно так:
/**
* @author Ilya Gazman
*/
public abstract class SynchronizedTask{
private ArrayList<Runnable> listeners = new ArrayList<Runnable>();
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000));
public final void await(Runnable listener){
synchronized (this) {
listeners.add(listener);
}
}
public void excecute(){
onExcecute();
for (int i = listeners.size() - 1; i >= 0; i--) {
Runnable runnable;
synchronized (this) {
runnable = listeners.remove(i);
}
threadPoolExecutor.execute(runnable);
}
}
protected abstract void onExcecute();
}
Ваше приложение будет использовать его следующим образом:
public class Test{
private Job job = new Job();
public Test() {
craeteSomeJobToRunInBackground();
methode1();
methode2();
}
private void methode1(){
System.out.println("Running methode 1");
job.await(new Runnable() {
@Override
public void run() {
System.out.println("Continue to running methode 1");
}
});
}
private void methode2(){
System.out.println("Running methode 2");
}
private void craeteSomeJobToRunInBackground() {
new Thread(new Runnable() {
@Override
public void run() {
job.excecute();
}
}).start();
}
private class Job extends SynchronizedTask{
@Override
protected void onExcecute() {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Job is done");
}
}
}