Может ли кто-нибудь объяснить, что await
делает функция?
Может ли кто-нибудь объяснить, что await
делает функция?
Ответы:
Они только вчера говорили об этом на PDC !
Await используется вместе с задачами (параллельное программирование) в .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();
В принципе, async
и await
ключевые слова позволяют указать , что выполнение метода должны останавливаться на все использованияawait
, которые обозначают асинхронные вызовы методов, а затем возобновить когда асинхронная операция завершена. Это позволяет вызывать метод в основном потоке приложения и обрабатывать сложную работу асинхронно без необходимости явно определять потоки и соединения или блокировать основной поток приложения.
Думайте об этом как о чем-то похожем на yield return
оператор в методе, создающем IEnumerable. Когда среда выполнения достигает yield
, она в основном сохраняет текущее состояние метода и возвращает полученное значение или ссылку. В следующий раз, когда IEnumerator.MoveNext () вызывается для возвращаемого объекта (который создается внутри средой выполнения), старое состояние метода восстанавливается в стеке, и выполнение продолжается со следующей строки после, yield return
как если бы мы никогда не покидали метод. Без этого ключевого слова тип IEnumerator должен быть настроен для хранения состояния и обработки итерационных запросов с помощью методов, которые могут действительно стать ОЧЕНЬ сложными.
Точно так же метод, помеченный как, async
должен иметь хотя бы один await
. Во await
время выполнения среда выполнения сохранит состояние текущего потока и стек вызовов, выполнит асинхронный вызов и вернется к циклу обработки сообщений среды выполнения, чтобы обработать следующее сообщение и сохранить реагирование приложения. Когда асинхронная операция завершена, при следующей возможности планирования стек вызовов для выполнения асинхронной операции возвращается и продолжается, как если бы вызов был синхронным.
Итак, эти два новых ключевых слова в основном упрощают кодирование асинхронных процессов, как и yield return
упрощают создание настраиваемых перечислимых элементов. Имея пару ключевых слов и немного базовых знаний, вы можете пропустить все запутанные и часто подверженные ошибкам детали традиционного асинхронного шаблона. Это будет НЕОБХОДИМ практически в любом приложении с графическим интерфейсом, управляемом событиями, например Winforms, WPF или Silverlight.
Принятый в настоящее время ответ вводит в заблуждение.
await
ничего не приостанавливает. Прежде всего, его можно использовать только в методах или лямбдах, отмеченных как 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
Выход Выполнять работу()
await
? Значит, если это вызывается из потока пользовательского интерфейса, он блокирует поток пользовательского интерфейса на 3 секунды? Идея этой модели - избегать подобных вещей.
Для всех, кто плохо знаком с асинхронным программированием в .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
и связал бы это с обратным вызовом, поэтому во время выполнения второй пример будет вести себя так же, как первый.
Может показаться, что это не экономит много работы, но когда дело доходит до таких вещей, как обработка исключений и контексты синхронизации, компилятор фактически делает за вас много тяжелой работы. Для получения дополнительной информации я бы порекомендовал ответы на часто задаваемые вопросы, за которыми следует серия блогов Стивена Клири .
Если бы мне пришлось реализовать это на 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");
}
}
}