Как и когда использовать «async» и «await»

778

Из моего понимания одна из главных вещей, которые async и await делают, это сделать код легким для записи и чтения - но использует их равными нерестующим фоновым потокам для выполнения длительной логики?

В настоящее время я тестирую самый простой пример. Я добавил несколько комментариев. Можете ли вы прояснить это для меня?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}
  • 0
    Вы можете получить идею, потому что недавно я задавал подобный вопрос. Вы можете найти его здесь: stackoverflow.com/questions/13159080/…
  • 32
    Также, в вашем примере обратите внимание, что вы получаете предупреждение при компиляции кода выше. Обратите внимание на предупреждение . Это говорит вам, что этот код не имеет смысла.
Показать ещё 2 комментария
Теги:
async-await
asynchronous

21 ответ

611
Лучший ответ

При использовании async и await компилятор генерирует конечный авто в фоновом режиме.

Вот пример, на который, я надеюсь, я могу объяснить некоторые детали высокого уровня, которые происходят:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

ОК, так что происходит здесь:

  • Task<int> longRunningTask = LongRunningOperationAsync(); начинает выполнение LongRunningOperation

  • Независимая работа выполняется, чтобы принять основной поток (Thread ID = 1), а затем await longRunningTask.

    Теперь, если longRunningTask еще не закончен и он все еще запущен, MyMethodAsync() вернется к его вызывающему методу, поэтому основной поток не блокируется. Когда выполняется longRunningTask, поток из ThreadPool (может быть любым потоком) вернется в MyMethodAsync() в предыдущем контексте и продолжит выполнение (в этом случае печать результата на консоль).

Второй случай состоял бы в том, что longRunningTask уже завершил выполнение, и результат будет доступен. При достижении await longRunningTask у нас уже есть результат, поэтому код будет продолжать выполняться в том же потоке. (в этом случае результат печати на консоль). Конечно, это не относится к приведенному выше примеру, где есть Task.Delay(1000).

  • 2
    Ни один поток не может обработать проблемы с пользовательским интерфейсом, хотя во многих примерах мы можем получить доступ к пользовательскому интерфейсу в коде после await, в вашем объяснении есть что-то не так
  • 1
    Я бывший программист VB6, и ожидание заставляет меня задуматься над инструкцией «Do Events», которую я использовал, чтобы сказать пользовательскому интерфейсу обновляться при выполнении интенсивного цикла. Прав ли я так думать?
Показать ещё 19 комментариев
134

В дополнение к другим ответам, посмотрите wait (ссылка С#)

и более конкретно в приведенном примере, это немного объясняет вашу ситуацию

Следующий пример Windows Forms иллюстрирует использование ожидания в асинхронный метод, WaitAsynchronouslyAsync. Сравните поведение этого метод с поведением WaitSynchronously. Без ожидания оператор, примененный к задаче, WaitSynchronously работает синхронно несмотря на использование модификатора async в его определении и вызов Thread.Sleep в своем теле.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}
  • 1
    Спасибо за ответ. Но выполняется ли WaitAsynchronouslyAsync () в отдельном потоке?
  • 24
    Я верю, что из раздела Ожидание выражение не блокирует поток, в котором оно выполняется. Вместо этого он заставляет компилятор зарегистрировать оставшуюся часть асинхронного метода как продолжение ожидаемой задачи. Затем управление возвращается к вызывающей стороне асинхронного метода. Когда задача завершается, она вызывает ее продолжение, и выполнение асинхронного метода возобновляется с того места, где оно было остановлено.
Показать ещё 5 комментариев
113

Из моего понимания одна из основных вещей, которые асинхронно и ждут, - это сделать код легким для записи и чтения.

Они должны сделать асинхронный код легким для записи и чтения, да.

Это то же самое, что и нерестовые потоки фона для выполнения длительной логики?

Совсем нет.

//Я не понимаю, почему этот метод должен быть помечен как "async".

Ключевое слово async включает ключевое слово await. Поэтому любой метод с использованием await должен быть помечен async.

//эта строка достигается после 5 секундного сна из метода DoSomethingAsync(). Разве это не должно быть достигнуто немедленно?

Нет, потому что методы async не запускаются по другому потоку по умолчанию.

//выполняется это в фоновом потоке?

Нет.


Вы можете найти мое async/await введение. Официальные документы MSDN также необычно хороши (особенно раздел TAP) и команда async внеклассный FAQ.

  • 4
    Так что он не работает в фоновом потоке, но также не блокируется. Это возможно благодаря асинхронным API, которые используют обратные вызовы вместо жонглирования потоками. Вы инициируете операцию (I / O, socket, ..) и возвращаетесь к выполнению своих задач. Когда операция будет завершена, ОС вызовет обратный вызов. Это то, что делает Node.js или среда Python Twisted, и у них тоже есть хорошее объяснение.
  • 3
    «Ключевое слово async включает ключевое слово await. Поэтому любой метод, использующий await, должен быть помечен как async.», - но почему? Этот ответ не помогает понять, почему метод должен быть помечен как асинхронный. Разве компилятор не может сделать вывод, что метод асинхронный, заглянув внутрь на ключевые слова await?
Показать ещё 3 комментария
105

объяснение

Вот краткий пример async/await на высоком уровне. Есть намного больше деталей, чтобы рассмотреть это.

Примечание. Task.Delay(1000) имитирует работу в течение 1 секунды. Я думаю, что лучше подумать об этом, ожидая ответа от внешнего ресурса. Поскольку наш код ожидает ответа, система может установить запущенную задачу в сторону и вернуться к ней после ее завершения. Между тем, он может выполнять некоторые другие работы по этой теме.

В приведенном ниже примере первый блок выполняет именно это. Он сразу запускает все задачи (линии Task.Delay) и устанавливает их в сторону. Код будет приостановлен на await a строки до тех пор, пока не будет выполнена 1 секунда, прежде чем перейти к следующей строке. Поскольку b, c, d и e все начали выполняться почти в то же время, что a (из-за отсутствия ожидания), они должны заканчиваться примерно в то же время в этом случае.

В приведенном ниже примере второй блок запускает задачу и ждет ее завершения (это то, что await), прежде чем запускать последующие задачи. Каждая итерация этого занимает 1 секунду. await во время паузы программы и ждать результата, прежде чем продолжить. Это основное различие между первым и вторым блоками.

пример

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

ВЫХОД:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Дополнительная информация о SynchronizationContext

Примечание. Здесь все становится немного туманным, поэтому, если я ошибаюсь во всем, пожалуйста, исправьте меня, и я обновлю ответ. Важно иметь базовое представление о том, как это работает, но вы можете обойтись, не будучи экспертом по нему, если вы никогда не используете ConfigureAwait(false), хотя, вероятно, вы, вероятно, проиграете какую-то возможность для оптимизации.

Есть один аспект этого, который делает концепцию асинхронного/ожидающего несколько сложнее понять. Тот факт, что в этом примере все это происходит в одном потоке (или, по крайней мере, то, что похоже на тот же поток в отношении его SynchronizationContext). По умолчанию await восстановит контекст синхронизации исходного потока, который был запущен. Например, в ASP.NET у вас есть HttpContext, привязанный к потоку, когда приходит запрос. Этот контекст содержит вещи, специфичные для исходного запроса Http, такого как исходный объект Request, который имеет такие вещи, как язык, IP-адрес, заголовки, и т.д. Если вы переключаете потоки на полпути через что-то, вы можете в конечном итоге попытаться извлечь информацию из этого объекта на другой HttpContext, который может быть катастрофическим. Если вы знаете, что не используете контекст ни для чего, вы можете выбрать "не заботиться" об этом. Это в основном позволяет вашему коду работать в отдельном потоке, не прибегая к контексту.

Как вы это достигаете? По умолчанию await a; код фактически делает предположение, что вы хотите захватить и восстановить контекст:

await a; //Same as the line below
await a.ConfigureAwait(true);

Если вы хотите, чтобы основной код продолжался в новом потоке без исходного контекста, вы просто используете false вместо true, чтобы он знал, что ему не нужно восстанавливать контекст.

await a.ConfigureAwait(false);

После того как программа будет приостановлена, она будет продолжаться потенциально в совершенно другом потоке с другим контекстом. Здесь будет улучшаться производительность - он может продолжаться в любом доступном потоке без необходимости восстановления исходного контекста, с которого он начинался.

Это путаница? Черт возьми! Вы можете понять это? Наверное! Как только вы поймете концепции, переходите к объяснениям Стивена Клири, которые, как правило, ориентированы на кого-то с техническим пониманием async/await уже.

  • 18
    Самый полезный пример, который я нашел в часах! Спасибо
  • 0
    Скажем, если все эти задачи возвращают int, и если я использую результат первой задачи во второй задаче (или какой-то расчет), это было бы неправильно?
Показать ещё 7 комментариев
45

Отображение приведенных выше объяснений в действии в простой консольной программе -

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

И результат:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Таким образом,

  • Main запускает длительный метод с помощью методов TestAsyncAwaitMethods. Это немедленно возвращается, не останавливая текущий поток, и мы сразу видим сообщение "Нажмите любую клавишу, чтобы выйти"
  • Все это время, LongRunningMethod работает в фоновом режиме. После его завершения другой поток из Threadpool подбирает этот контекст и отображает окончательное сообщение

Таким образом, нить не блокируется.

  • 0
    «Нажмите любую клавишу для выхода ...» будет показано, в какой части вывода?
  • 0
    и что за использование (возврат 1)? это необходимо?
35

Я думаю, что вы выбрали плохой пример с System.Threading.Thread.Sleep

Точка async Задача состоит в том, чтобы она выполнялась в фоновом режиме без блокировки основного потока, например, с помощью DownloadFileAsync

System.Threading.Thread.Sleep - это не то, что "делается", он просто спит, и поэтому ваша следующая строка будет достигнута через 5 секунд...

Прочитайте эту статью, я думаю, что это отличное объяснение концепции async и await: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

  • 0
    Почему Sleep - плохой пример, а Download - хороший пример. Это похоже на FooBar, когда я вижу Thread.Sleep, я понимаю, что есть какая-то задача, которая требует времени. Думаю его вопрос актуален
17

Вот краткая консольная программа, которая поможет понять, кто последует. Метод "TaskToDo" - это ваш длинный метод, который вы хотите сделать async. Выполнение его запуска Async выполняется методом TestAsync. Метод тестовых циклов просто проходит через задачи TaskToDo и запускает их Async. Вы можете видеть это в результатах, потому что они не завершаются в том же порядке от запуска до запуска - они сообщают о потоке пользовательского интерфейса консоли при их завершении. Упрощенный, но я думаю, что упрощенные примеры лучше всего раскрывают ядро ​​шаблона, чем более привлекательные примеры:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}
11

Этот ответ предназначен для предоставления некоторой информации, специфичной для ASP.NET.

Используя async/wait в контроллере MVC, можно увеличить использование пула потоков и добиться гораздо большей пропускной способности, как описано в следующей статье,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

В веб-приложениях, которые видят большое количество одновременных запросов в пуск или имеет взрывную нагрузку (где concurrency внезапно увеличивается), асинхронность этих веб-сервисов приведет к увеличению отзывчивость вашего приложения. Асинхронный запрос принимает такое же количество времени для обработки как синхронный запрос. Например, если запрос вызывает вызов веб-службы, для которого требуется две секунды, чтобы завершено, запрос занимает две секунды, выполняется ли это синхронно или асинхронно. Однако во время асинхронного вызова, поток не блокируется от ответа на другие запросы, пока он ждет завершения первого запроса. Следовательно, асинхронный запросы предотвращают очередь запросов и рост пула потоков, когда есть много одновременных запросов, которые вызывают длительные операции.

10

Все ответы здесь используют функцию Task.Delay() или другую встроенную функцию async. Но вот мой пример, который не использует ни одну из этих асинхронных функций:

    // Starts counting to a large numbewr and then immediately displays message "i'm counting...". 
    // Then it waits for task to finish and displays "finished, press any key".
    static void asyncTest ()
    {
        Console.WriteLine("Started asyncTest()");
        Task<long> task = asyncTest_count();
        Console.WriteLine("Started counting, please wait...");
        task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
        //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
        Console.WriteLine("Finished counting.");
        Console.WriteLine("Press any key to exit program.");
        Console.ReadLine();
    }

    static async Task<long> asyncTest_count()
    {
        long k = 0;
        Console.WriteLine("Started asyncTest_count()");
        await Task.Run(() =>
        {
            long countTo = 100000000;
            int prevPercentDone = -1;
            for (long i = 0; i <= countTo; i++)
            {
                int percentDone = (int)(100 * (i / (double)countTo));
                if (percentDone != prevPercentDone)
                {
                    prevPercentDone = percentDone;
                    Console.Write(percentDone.ToString() + "% ");
                }

                k = i;
            }
        });
        Console.WriteLine("");
        Console.WriteLine("Finished asyncTest_count()");
        return k;
    }
  • 1
    Спасибо! первый ответ, который на самом деле делает некоторую работу вместо ожидания.
  • 0
    спасибо за показ task.Wait(); и как это можно использовать, чтобы избежать асинхронного / ожидающего ада: P
9

Честно говоря, я по-прежнему считаю, что лучшим объяснением является будущее и promises в Википедии: http://en.wikipedia.org/wiki/Futures_and_promises

Основная идея заключается в том, что у вас есть отдельный пул потоков, выполняющий задачи асинхронно. При использовании. Однако объект обещает, что он выполнит операцию в течение некоторого времени и даст вам результат, когда вы его попросите. Это означает, что он будет блокироваться, когда вы запрашиваете результат и не закончили, но в противном случае выполните пул потоков.

Оттуда вы можете оптимизировать вещи: некоторые операции могут быть реализованы async, и вы можете оптимизировать такие вещи, как IO файла и сетевую связь, путем объединения последующих запросов и/или их переупорядочения. Я не уверен, что это уже входит в структуру задач Microsoft, но если это не так, это будет одна из первых вещей, которые я бы добавил.

Фактически вы можете реализовать сортировку будущего шаблона с доходностью в С# 4.0. Если вы хотите точно знать, как это работает, я могу порекомендовать эту ссылку, которая выполняет достойную работу: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/. Однако, если вы начнете играть с ним самостоятельно, вы заметите, что вам действительно нужна поддержка языка, если вы хотите делать все классные вещи - это именно то, что сделала Microsoft.

6

См. Этот скрипт https://dotnetfiddle.net/VhZdLU (и, если это возможно, улучшить его) для запуска простого консольного приложения, которое показывает использование Task, Task.WaitAll(), async и ожидание операторов в одной и той же программе.

Эта скрипка должна очистить концепцию цикла выполнения.

Вот пример кода

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Трассировка из окна вывода: Изображение 999

  • 0
    приятное объяснение!
4

Для быстрого обучения..

  • Понимание потока выполнения метода (со схемой): 3 минуты

  • Вопрос интроспекции (обучение ради): 1 мин.

  • Быстро пройти синтаксический сахар: 5 минут

  • Поделитесь путаницей разработчика: 5 минут

  • Проблема: быстро измените реальную реализацию обычного кода на код Async: 2 минуты

  • Где дальше?

Понимание потока выполнения метода (со схемой): 3 минуты

На этом изображении просто сосредоточьтесь на # 6 Изображение 1000

На шаге # 6: AccessTheWebAsync() закончил работу, которую он может сделать без результата getStringTask. Поэтому AccessTheWebAsync использует ожидающий оператор, чтобы приостановить его прогресс и вернуть управление (выход) вызывающему. AccessTheWebAsync возвращает вызывающей программе задание (возвращаемое значение строки). Задача представляет собой обещание произвести строковый результат. Но когда он вернет звонок? второй вызов снова?

Вызывающий AccessAWebAsync() ничего не делал, кроме как ждать (он мог бы выполнять некоторые внутренние задачи, а затем ждать, если это необходимо). Таким образом, вызывающий абонент ждет AccessTheWebAsync и AccessTheWebAsync ждет GetStringAsync на данный момент.

Помните, что метод уже был возвращен, он не может вернуться снова (второй раз). Итак, как узнает собеседник? Это все о задачах! Задача была возвращена. Задание ожидалось (не метод, а не значение). Значение будет задано в Задаче. Статус задачи будет установлен. Caller просто контролирует задачу. Далее читает для последующего использования здесь.

Вопрос интроспекции для изучения: 1 мин.

Давайте немного подкорректируем вопрос:

Как и когда использовать async и await Tasks?

Поскольку учебная Task автоматически охватывает другую 2. Ради обучения, по крайней мере. Конечно, это ответ на ваш вопрос об async и await.

Быстро пройти синтаксический сахар: 5 минут

  • Перед конверсией (исходный метод)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO();//Do some long running IO. return result; }

  • Другой метод Task-ified для вызова вышеуказанного метода

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start();//Hot task (started task) should always be returned. return task; }

Мы упоминали об ожидании или асинхронности? Нет. Вызовите вышеуказанный метод, и вы получите задание. Что вы можете контролировать. Вы уже знаете, что возвращает задача. Integer.

  • Вызов задачи несколько сложный. Назовем метод MethodTask()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Мы ожидаем, что задача будет завершена. Отсюда await. Поскольку мы используем await, мы должны использовать async (обязательный) и MethodAsync с "Async" в качестве префикса (стандарт кодирования). Далее далее читает здесь

Поделитесь путаницей разработчика: 5 минут

Разработчик ошибся в том, что не выполнил Task но он все еще работает! Попытайтесь понять этот вопрос и только принятый ответ здесь. Надеюсь, вы прочитали и полностью поняли. Аналогично в нашем примере вызов уже построенного MethodAsync() проще, чем реализация этого метода с помощью Task (MethodTask()). Большинство разработчиков затрудняют задачу поиска Tasks при преобразовании кода в асинхронный.

Совет. Попробуйте найти существующую реализацию Async (например, MethodAsync или ToListAsync), чтобы MethodAsync ToListAsync. Поэтому нам нужно иметь дело только с Async и ждать (что легко и довольно похоже на обычный код)

Проблема: быстро изменить реальную реализацию обычного кода для работы Async: 2 минуты

Строка кода, показанная ниже в слое данных, начала прерываться (во многих местах). Потому что мы обновили часть нашего кода с.Net framework 4.2 до ядра.Net. Мы должны были исправить это за 1 час по всему приложению!

var myContract = query.Where(c => c.ContractID == _contractID).First();

очень просто!

  1. EntityFrameWork nuget (имеет QueryableExtensions)
  2. namespace = Microsoft.EntityFrameworkCore

код был изменен следующим образом

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. Подпись метода изменилась с

    Contract GetContract(int contractnumber)

    в

    async Task<Contract> GetContractAsync(int contractnumber)

  2. вызываемый метод также получил влияние: GetContractAsync(123456); был вызван как GetContractAsync(123456).Result;

  3. Мы изменили его всюду за 30 минут!

Но архитектор сказал нам не использовать библиотеку EntityFrameWork только для этого! упс! драма! Затем мы создали пользовательскую задачу. Который вы знаете как. Все еще легко!

Где дальше? Существует замечательное быстрое видео, которое мы могли бы посмотреть о преобразовании синхронных вызовов в асинхронный в ядре ASP.Net, потому что это, скорее всего, направление, которое можно было бы прочитать после прочтения этого.

3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}
2

То, как я понимаю, также должно быть добавлено третье слово, добавленное в микс: Task.

Async - это просто определитель, который вы наложили на свой метод, чтобы сказать это асинхронный метод.

Task является возвратом функции Async. Он выполняется асинхронно.

Вы await Задача. Когда выполнение кода достигает этой строки, управление переходит к вызывающей стороне вашей исходной функции.

Если вместо этого вы назначаете возврат функции Async (т.е. Task) в переменную, когда выполнение кода достигает этой строки, оно просто продолжается после этой строки в окружающей функции, а Task выполняется асинхронно.

1

Async/Await

На самом деле Async/Await - это пара ключевых слов, которые являются просто синтаксическим сахаром для создания обратного вызова асинхронной задачи.

Возьмем для примера эту операцию:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

Этот метод имеет несколько недостатков. Ошибки не передаются, и это очень трудно читать. Но Async и Await приходят, чтобы помочь нам:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Ожидать вызовы должны быть в асинхронных методах. Это имеет некоторые преимущества:

  • Возвращает результат задания
  • автоматически создает обратный вызов
  • проверяет ошибки и позволяет им всплывать в стеке вызовов (только до не ожидающих вызовов в стеке вызовов)
  • ждет результата
  • освобождает основной поток
  • запускает обратный вызов в главном потоке
  • использует рабочий поток из пула потоков для выполнения задачи
  • делает код легко читаемым
  • и многое другое

ПРИМЕЧАНИЕ. Async и Await используются с асинхронными вызовами, чтобы их не делать. Для этого вы должны использовать Task Libary, например Task.Run().

Вот сравнение между ожидающими и никого не ожидающими решениями

Это не асинхронное решение:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        Thread.Sleep(1000);
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Это асинхронный метод:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

На самом деле вы можете вызвать асинхронный метод без ключевого слова await, но это означает, что любое исключение здесь проглатывается в режиме выпуска:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async и Await не предназначены для параллельных вычислений. Они используются, чтобы не блокировать ваш основной поток. Если это про asp.net или приложения Windows. Блокировка вашего основного потока из-за сетевого вызова - это плохо. Если вы сделаете это, ваше приложение перестанет отвечать на запросы или может зависнуть.

Проверьте MS Docs, чтобы получить некоторые примеры.

  • 1
    Это кажется неправильным. В асинхронном решении «none» оно будет приостановлено на 1 секунду, поскольку спящий поток будет выполняться в основном потоке.
1

На более высоком уровне:

1) Ключевое слово Async позволяет ожидание и все, что он делает. Ключевое слово Async не запускает метод в отдельном потоке. Асинхронный метод begin f запускается синхронно до тех пор, пока он не достигнет ожиданий по трудоемкой задаче.

2) Вы можете ждать метода, который возвращает Task или Task типа T. Вы не можете ждать по методу async void.

3) В тот момент, когда основные встречные потоки ждут по трудоемкой задаче или когда начинается фактическая работа, основной поток возвращается к вызывающей стороне текущего метода.

4) Если основной поток ожидает выполнения задачи, которая все еще выполняется, она не ждет его и возвращается к вызывающей стороне текущего метода. Таким образом, приложение остается отзывчивым.

5) Ожидание выполнения задачи обработки теперь будет выполняться в отдельном потоке из пула потоков.

6) Когда эта задача ожидания будет завершена, весь код под ней будет выполняться отдельным потоком

Ниже приведен пример кода. Выполните его и проверьте идентификатор потока

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}
1

В следующем коде метод HttpClient GetByteArrayAsync возвращает задачу, getContentsTask. Задачей является обещание создать массив байтов, когда задача будет завершена. Оператор ожидания применяется к getContentsTask для приостановки выполнения в SumPageSizesAsync до тех пор, пока getContentsTask не будет завершен. Тем временем управление возвращается вызывающему абоненту SumPageSizesAsync. Когда getContentsTask закончен, выражение ожидания вычисляет байтовый массив.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}
1

использует их равными нерестовым фоновым потокам для выполнения длинных логика продолжительности?

Эта статья MDSN: асинхронное программирование с асинхронным и ожидающим (С#) объясняет это явно:

Асинхронные и ожидающие ключевые слова не вызывают дополнительных потоков создано. Асинхронные методы не требуют многопоточности, поскольку асинхронный метод не запускается в своем потоке. Метод работает на текущем синхронизации и использует время в потоке только тогда, когда метод активен.

0

Async & Await Simple Explanation

Простая аналогия

Человек может ждать своего утреннего поезда. Это все, что они делают, поскольку это их основная задача, которую они выполняют в настоящее время. (синхронное программирование (что вы обычно делаете!))

Другой человек может ждать своего утреннего поезда, пока он курит сигарету, а затем пьет кофе. (Асинхронное программирование)

Что такое асинхронное программирование?

Асинхронное программирование - это когда программист решает запустить часть своего кода в отдельном потоке от основного потока выполнения, а затем уведомить основной поток о его завершении.

Что на самом деле делает ключевое слово async?

Префикс асинхронного ключевого слова к имени метода, например

async void DoSomething(){ . . .

позволяет программисту использовать ключевое слово await при вызове асинхронных задач. Это все, что он делает.

Почему это важно?

Во многих программных системах основной поток зарезервирован для операций, конкретно относящихся к пользовательскому интерфейсу. Если на моем компьютере запущен очень сложный рекурсивный алгоритм, выполнение которого занимает 5 секунд, но я запускаю его в главном потоке (поток пользовательского интерфейса). Когда пользователь пытается щелкнуть что-либо в моем приложении, оно будет заморожено. поскольку мой основной поток поставлен в очередь и в настоящее время обрабатывает слишком много операций. В результате основной поток не может обработать щелчок мыши, чтобы запустить метод от нажатия кнопки.

Когда вы используете Async и Await?

Идеально используйте асинхронные ключевые слова, когда вы делаете что-то, что не связано с пользовательским интерфейсом.

Допустим, вы пишете программу, которая позволяет пользователю делать наброски на своем мобильном телефоне, но каждые 5 секунд он будет проверять погоду в Интернете.

Мы должны ждать звонка, который каждые 5 секунд вызывает опрос, чтобы узнать погоду, так как пользователь приложения должен продолжать взаимодействовать с мобильным сенсорным экраном, чтобы рисовать красивые картинки.

Как вы используете Async и Await

Следуя вышеприведенному примеру, вот некоторый псевдокод того, как его написать:

     //ASYNCHRONOUS
    //this is called every 5 seconds
    async void CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }
0

Ниже приведен код, который читает файл Excel, открывая диалоговое окно, а затем использует асинхронное ожидание для запуска асинхронного кода, который читает одну строку из Excel и привязывается к сетке.

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}
0

Ответы здесь полезны как общее руководство по await/async. Они также содержат некоторые подробности о том, как await/async подключен. Я хотел бы поделиться с вами некоторым практическим опытом, который вы должны знать перед использованием этого шаблона проектирования.

Термин "ожидание" имеет литеральный характер, поэтому любой поток, который вы вызываете, будет ждать результата метода до продолжения. На переднем плане это катастрофа. Передняя часть несет ответственность за создание вашего приложения, включая представления, модели просмотра, начальную анимацию и все остальное, что вы загрузили с помощью этих элементов. Поэтому, когда вы ожидаете поток переднего плана, вы останавливаете приложение. Пользователь ждет и ждет, когда ничего не произойдет. Это обеспечивает отрицательный пользовательский интерфейс.

Вы можете наверняка ждать фоновый поток, используя различные средства:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

Полный код для этих замечаний находится по адресу https://github.com/marcusts/xamarin-forms-annoyances. См. Решение под названием AwaitAsyncAntipattern.sln.

Сайт GitHub также содержит ссылки на более подробное обсуждение этой темы.

  • 1
    Из того, что я понимаю, async / await является синтаксическим сахаром для обратных вызовов, он не имеет ничего общего с многопоточностью. msdn.microsoft.com/en-us/magazine/hh456401.aspx Он предназначен для кода без привязки к ЦП, например, ожидание ввода или задержка. Task.Run следует использовать только для кода с привязкой к процессору blog.stephencleary.com/2013/10/…
  • 0
    The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing. Это не правда - может быть, вы имели в виду Task.Wait ()? Когда вы используете await , он устанавливает остальную часть метода как продолжение, которое будет выполнено, когда все, что вы ожидаете, будет завершено. Он выходит из метода, в котором вы его использовали, чтобы вызывающий мог продолжить. Затем, когда ожидаемая строка фактически завершена, она завершает оставшуюся часть этого метода в каком-то потоке (обычно в рабочем потоке).
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню