it-swarm.xyz

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

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

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

// 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;
}
830
Dan Dinu

При использовании 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;
}

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

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

  2. Независимая работа сделана, давайте предположим, что основной поток (ID потока = 1), тогда await longRunningTask достигнут. 

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

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

629
Dan Dinu

В дополнение к другим ответам взгляните на await (C # Reference)

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

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

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";
}
142
Adriaan Stander

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

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

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

Не за что.

// Я не понимаю, почему этот метод должен быть помечен как «асинхронный».

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

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

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

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

Нет.


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

122
Stephen Cleary

Объяснение

Вот быстрый пример 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, такие как исходный объект запроса, который имеет такие вещи, как язык, IP-адрес, заголовки, и т.д. Если вы переключаете потоки на полпути при обработке чего-либо, вы можете в конечном итоге попытаться извлечь информацию из этого объекта в другом HttpContext, что может иметь катастрофические последствия. Если вы знаете, что ни для чего не будете использовать контекст, вы можете выбрать «не заботиться» об этом. Это в основном позволяет вашему коду запускаться в отдельном потоке, не приводя к этому контекст.

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

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

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

await a.ConfigureAwait(false);

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

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

116
Joe Phillips

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

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...

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

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

Таким образом, не поток заблокирован. 

47
sppc42

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

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

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

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

38
Vnuk

Вот быстрая консольная программа, чтобы она была понятна тем, кто следит за ней. Метод TaskToDo - это ваш долгосрочный метод, который вы хотите сделать асинхронным. Запуск Async выполняется методом TestAsync. Метод test loop просто запускает задачи 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);
        }
    }
}
17
MarkWalls

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

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

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

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

11
Lex Li

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

    // 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;
    }
10
Tone Škoda

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

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

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

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

9
atlaste

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

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

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

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;
    }   

}

Трассировка из окна вывода: enter image description here

6
vibs2006

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

  • Разобраться в потоке выполнения метода (с диаграммой): 3 минуты

  • Вопрос самоанализ (ради обучения): 1 мин

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

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

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

  • Куда дальше?

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

На этом изображении просто сфокусируйтесь на # 6  enter image description here

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

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

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

Вопрос самоанализ для изучения сакэ: 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; }

Мы упомянули await или async? Нет. Вызовите вышеуказанный метод, и вы получите задание. Который вы можете контролировать. Вы уже знаете, что возвращает задание .. целое число.

  • Вызов задачи немного сложен. Давайте назовем 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), чтобы передать трудность на аутсорсинг. Поэтому нам нужно иметь дело только с Async и await (что легко и очень похоже на обычный код)

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

Строка кода, показанная ниже в Data Layer, начала ломаться (много мест). Потому что мы обновили часть нашего кода с .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 Core , потому что это, вероятно, направление, в котором можно было бы пойти после прочтения этого. 

4
Blue Clouds
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;
    }
}
3
Weslley Rufino de Lima

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

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

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();
        }

    }
}
3
ABajpai

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

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

Task - это возвращение функции async. Это выполняется асинхронно.

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

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

2
user21306

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

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
lazydeveloper

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");

    }

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

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

NOTE: Async и Await используются with асинхронные вызовы not для их выполнения. Для этого вы должны использовать 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
Hakim

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

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

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

1
Dmitry G.

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

  1. Асинхронная функция возвращает обещание. Обратное также верно. Каждая функция, которая возвращает обещание, может рассматриваться как асинхронная функция. Await используется для вызова асинхронной функции и ожидания ее разрешения или отклонения.

  2. await блокирует выполнение кода в асинхронной функции, в которой он находится. Если вывод function2 зависит от выхода function1, тогда я использую await.

  3. Если две функции могут выполняться параллельно, создайте две разные асинхронные функции и затем запустите их параллельно.

  4. Для параллельного запуска обещаний создайте массив обещаний, а затем используйте Promise.all (promisesArray). Каждый раз, когда вы используете await, помните, что вы пишете код блокировки. Со временем мы склонны пренебрегать этим.

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

  6. Если ваш код содержит код блокировки, лучше сделать его асинхронным. Делая это, вы убедитесь, что кто-то другой может использовать вашу функцию асинхронно.

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

Надеюсь, что это поможет вам легко решить, когда использовать обещания и когда использовать обещания и когда использовать async-await

0
Sunny Sultan

Ниже приведен код, который читает файл 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
Zaheer Abbas

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
James Mallon

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

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

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

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 также есть ссылки на более подробное обсуждение этой темы.

0
Stephen Marcus