Показаны сообщения с ярлыком await. Показать все сообщения
Показаны сообщения с ярлыком await. Показать все сообщения

вторник, 25 ноября 2014 г.

await async from ms

async await

http://habrahabr.ru/post/139734/

Kлючевое слово async не приводит к тому, что метод будет выполняться в фоновом потоке (как кажется из названия), а только отмечает, что внутри метода присутствует await, который работает с Task и Task<T> таким образом, что остаток метода после await будет выполнен после завершения Task, но в основном потоке.

http://habrahabr.ru/post/107498/

http://habrahabr.ru/post/107583/

http://habrahabr.ru/post/108184/


Synchronization Context

http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I

http://www.codeproject.com/Articles/32113/Understanding-SynchronizationContext-Part-II

http://www.codeproject.com/Articles/32119/Understanding-SynchronizationContext-Part-III

вторник, 5 ноября 2013 г.

async, await

http://stackoverflow.com/questions/14455293/async-and-await


The following Windows Forms example illustrates the use of await in an async method, WaitAsynchronouslyAsync. Contrast the behavior of that method with the behavior of WaitSynchronously. Without an await operator applied to a task, WaitSynchronously runs synchronously despite the use of the async modifier in its definition and a call to Thread.Sleep in its body.
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";
}

async await


http://habrahabr.ru/post/162353/

12 декабря 2012 в 16:07

Использование async и await в C# — лучшие практикиперевод tutorial


Ключевые слова async и await, введённые в C# 5.0, значительно упрощают асинхронное программирование. Они также скрывают за собой некоторые сложности, которые, если вы потеряете бдительность, могут добавить проблем в ваш код. Описанные ниже практики пригодятся вам, если вы создаёте асинхронный код для .NET приложений.


Используйте async /await только для тех мест, которые могут длиться «долго»

Здесь всё просто. Создание Task и других структур для управления асинхронными операциями добавляет некоторые накладные расходы. Если ваша операция действительно продолжительна, например выполнение IO запроса, тогда эти расходы в основном не будут заметны. А в том случае, если ваша операция коротка или займёт несколько циклов процессора, тогда возможно будет лучше выполнять эту операцию синхронно.

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

Предпочитайте async/await вместо Task

Написание асинхронного кода, используя async/await, намного упрощает и сам процесс создания кода, и его чтение, нежели использование задач Task.

public Task<Data> GetDataAsync()
{
    return MyWebService.FetchDataAsync()
        .ContinueWith(t => new Data (t.Result));
}


public async Task<Data> GetDataAsync()
{
    var result = await MyWebService.FetchDataAsync();
    return new Data (result);
}

В терминах производительности, оба метода, представленные выше, имеют небольшие накладные расходы, но они несколько по-разному масштабируются при увеличении количества задач в них:
  • Task строит цепочку продолжений, которая увеличивается в соответствии с количеством задач, связанных последовательно, и состояние системы управляется через замыкания, найденные компилятором.
  • Async/await строит машину состояний, которая не использует дополнительных ресурсов при добавлении новых шагов. Однако компилятор может определить больше переменных для сохранение в стеки машины состояний, в зависимости от вашего кода (и компилятора). В статье на MSDN отлично расписаны детали происходящего.

В большинстве сценариев async/await будет использовать меньше ресурсов и выполняться быстрее, чем задачиTask.

Используйте уже выполненную пустую статическую задачу для условного кода

Иногда вы хотите запустить задачу только при каком-то условии. К сожалению, await вызоветNullReferenceException, если получит null вместо задачи, а обработка этого сделает ваш код менее читабельным.

public async Task<Data> GetDataAsync(bool getLatestData)
{
    Task<WebData> task = null;
    if (getLatestData)
        task = MyWebService.FetchDataAsync();

    // здесь выполним другую работу

    // и не забудем проверить на null
    WebData result = null;
    if (task != null)
        result = await task;

    return new Data (result);
}

Один из способов немного упростить код – использовать пустую задачу, которая уже выполнена. Полученный код будет чище:

public async Task<Data> GetDataAsync(bool getLatestData)
{
    var task = getLatestData ? MyWebService.FetchDataAsync() : Empty<WebData>.Task;

    // здесь выполним другую работу

    // task всегда не null
    return new Data (await task);
}

Убедитесь, что задача является статической и создана как завершённая. Например:

public static class Empty<T>
{
    public static Task<T> Task { get { return _task; } }    

    private static readonly Task<T> _task = System.Threading.Tasks.Task.FromResult(default(T));
}


Производительность: предпочитайте кэшировать сами задачи, нежели их данные

Существую некоторые накладные расходы при создании задач. Если вы кэшируете ваши результаты, но потом конвертируете их обратно в задачи, вы, возможно, создаете дополнительные объекты задач.

public Task<byte[]> GetContentsOfUrl(string url)
{
    byte[] bytes;

    if (_cache.TryGetValue(url, out bytes))
        // дополнительная задача создаётся здесь
        return Task<byte[]>.Factory.StartNew(() => bytes);

    bytes = MyWebService.GetContentsAsync(url)
        .ContinueWith(t => { _cache.Add(url, t.Result); return t.Result; );
}
// это не потокобезоспасно (не копируйте себе этот код как есть)
private static Dictionary<string, byte[]> _cache = new Dictionary<string, byte[]>();

Вместо этого будет лучше копировать в кэш сами задачи. В этом случае использующий их код может ждать уже выполненную задачу. В Task Parallel Library присутствуют оптимизации для того, чтобы код ожидающий выполнения уже завершённой задачи выполнялся быстрее.

public Task<byte[]> GetContentsOfUrl(string url)
{
    Task<byte[]> bytes;

    if (!_cache.TryGetValue(url, out bytes))
    {
        bytes = MyWebService.GetContentsAsync(url);
        _cache.Add(url, bytes);
    }

    return bytes;
}
//  это не потокобезоспасно (не копируйте себе этот код как есть)
private static Dictionary<string, Task<byte[]>> _cache = new Dictionary<string, Task<byte[]>>();


Производительность: понимайте, как await сохраняет состояние

Когда вы используете async/await, компилятор создаёт машину состояний, которая хранит переменные и стек. Например:

public static async Task FooAsync()
{
  var data = await MyWebService.GetDataAsync();
  var otherData = await MyWebService.GetOtherDataAsync();
  Console.WriteLine("{0} = "1", data, otherdata);
}

Это создаст объект состояния с несколькими переменными. Смотрите, как компилятор сохранит переменные метода:

[StructLayout(LayoutKind.Sequential), CompilerGenerated]
private struct <FooAsync>d__0 : <>t__IStateMachine {
  private int <>1__state;
  public AsyncTaskMethodBuilder <>t__builder;
  public Action <>t__MoveNextDelegate;
 
  public Data <data>5__1;
  public OtherData <otherData>5__2;
  private object <>t__stack;
  private object <>t__awaiter;
 
  public void MoveNext();
  [DebuggerHidden]
  public void <>t__SetMoveNextDelegate(Action param0);
}

Замечание 1. Если вы декларируете переменную, она сохранится в объекте, хранящем состояние. Это может привести к тому, что объекты будут оставаться в памяти дольше, чем вы бы могли ожидать.

Замечание 2. Но если вы не станете декларировать переменную, а использовать значение Async вызова вместе сawait, переменная попадёт во внутренний стек:

public static async Task FooAsync()
{
  var data = MyWebService.GetDataAsync();
  var otherData = MyWebService.GetOtherDataAsync();

  // промежуточные результаты попадут во внутренний стек и 
  // добавятся дополнительные переключения контекстов между await-ами
  Console.WriteLine("{0} = "1", await data, await otherdata);
}

Вы не должны слишком сильно волноваться по этому поводу до тех пор, пока вы не видите проблем производительности. Если вы всё-таки решили углубиться в оптимизацию, на MSDN есть хорошая статья по этому поводу: Async Performance: Understanding the Costs of Async and Await.

Стабильность: async/await – это не Task.Wait

Машина состояний, генерируемая async/await – это не то же самое, что Task.ContinueWith/Wait. В общем случае вы можете заменить реализацию с Task на await, но могут возникнуть некоторые проблемы производительности и стабильности. Давайте посмотрим подробнее.

Стабильность: знайте свой контекст синхронизации

Код .NET всегда исполняется в некотором контексте. Этот контекст определяет текущего пользователя и другие значения, требуемые фреймворком. В некоторых контекстах выполнения, код работает в контексте синхронизации, который управляет выполнением задач и другой асинхронной работы.

По-умолчанию, после await код продолжит работать в контексте, в котором он был запущен. Это удобно, потому что в основном вы захотите, чтобы контекст безопасности был восстановлен, и вы хотите, чтобы ваш код после await имел доступ к объектам Windows UI, если он уже имел доступ к ним при старте. Заметим, чтоTask.Factory.StartNew – не осуществляет восстановление контекста.

Некоторые контексты синхронизации не поддерживают повторный вход в них и являются однопоточными. Это означает, что только одна единица работы может выполняться в этом контексте одновременно. Примером этого может быть поток Windows UI или контекст ASP.NET.

В таких однопоточных контекстах синхронизации довольно легко получить deadlock. Если вы создадите задачу в однопоточном контексте, и потом будете ждать в этом же контексте, ваш код, который ждёт, будет блокировать выполнение фоновой задачи.

public ActionResult ActionAsync()
{
    // DEADLOCK: это блокирует асинхронную задачу
    // которая ждёт, когда она сможет выполняться в этом контексте
    var data = GetDataAsync().Result;

    return View(data);
}

private async Task<string> GetDataAsync()
{
    // простой вызов асинхронного метода
    var result = await MyWebService.GetDataAsync();
    return result.ToString();
}


Стабильность: не используйте Wait, чтобы дождаться окончания задачи прямо здесь

Как основное правило – если вы создаёте асинхронный код, будьте осторожны c использованием Wait. (c awaitвсё несколько лучше.)

Не используйте Wait для задач в однопоточных контекстах синхронизации, таких как:
  • Потоки UI
  • Контекст ASP.NET

Хорошая новость заключается в том, что фреймворк позволяет вам возвращать Task в определённых случаях, и сам фреймворк будет ожидать выполнения задачи. Доверье ему этот процесс:

public async Task<ActionResult> ActionAsync()
{
    // этот метод использует async/await и возвращает Task
    var data = await GetDataAsync();

    return View(data);
}

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

Стабильность: рассмотрите использование ConfigureAwait, если вы создаёте библиотеку

Если вы обязаны ожидать выполнения задачи в одном из этих контекстов, вы можете использоватьConfigureAwait, чтобы сказать системе, что она не должна выполнять фоновую задачу в вашем контексте. Недостатком этого является то, что фоновая задача не будет иметь доступа к тому же самому контексту синхронизации, так что вы потеряете доступ к Windows UI или HttpContext (хотя ваш контекст безопасности всё равно будет у вас).

Если вы создаёте «библиотечную» функцию, которая возвращает Task, вы, скорее всего, не знаете, как она будет вызываться. Так что, возможно, будет безопаснее добавить ConfigureAwait(false) к вашей задаче перед тем как её вернуть.

private async Task<string> GetDataAsync()
{
    // ConfigureAwait(false) говорит системе, чтобы она
    // позволила оставшемуся коду выполняться в любом контексте
    var result = await MyWebService.GetDataAsync().ConfigureAwait(false);
    return result.ToString();
}


Стабильность: понимайте, как ведут себя исключения

Когда смотришь на асинхронный код, тяжело иногда сказать, что же случается с исключениями. Будет ли оно передано вызывающей функции, или тому коду, который ждёт выполнения задачи?

Правила в этом случае довольно прямолинейны, но всё равно иногда трудно ответить на вопрос, просто глядя на код.

Некоторые примеры:
  • Исключения, вызванные из самого async/await метода, будут отправлены коду, ожидающему выполнения задачи (awaiter).
    public async Task<Data> GetContentsOfUrl(string url)
    {
        // это исключение будет вызвано на коде, ожидающем 
        // выполнения этой задачи
        if (url == null) throw new ArgumentNullException();
    
        var data = await MyWebService.GetContentsOfUrl();
        return data.DoStuffToIt();
    }
    
  • Исключения, вызванные из делегата задачи Task, тоже будут отправлены коду, ожидающему выполнения задачи (awaiter).
    public Task<Data> GetContentsOfUrl(string url)
    {
        return Task<Data>.Factory.StartNew(() =>
        {
            // это исключение будет вызвано на коде, ожидающем 
            // выполнения этой задачи
            if (url == null) throw new ArgumentNullException();
    
            var data = await MyWebService.GetContentsOfUrl();
            return data.DoStuffToIt();
        }
    }
    
  • Исключения, вызванные во время создания Task, будут отправлены коду, который вызывал этот метод (caller) (что, в общем, очевидно):
    public Task<Data> GetContentsOfUrl(string url)
    {
        // это исключение будет вызвано на вызывающем коде
        if (url == null) throw new ArgumentNullException();
    
        return Task<Data>.Factory.StartNew(() =>
        {
            var data = await MyWebService.GetContentsOfUrl();
            return data.DoStuffToIt();
        }
    }
    

Последний пример является одной из причин, почему я предпочитаю async/await вместо создания цепочек задач посредством Task.

Дополнительные ссылки (на английском)