2016-02-16 2 views
9

Я читал много о async/await, но у меня все еще есть недостаток в понимании следующей ситуации.Борьба с async/wait C#

Мой вопрос в том, должен ли я реализовать свои методы «обертки», как в DoSomething(), или как в DoSomethingAsync().

Итак, что лучше (и почему): Использую ли я await в методах обертки или напрямую возвращаю задачу?

 public static async void Main() 
     { 
      await DoSomething(); 
      await DoSomethingAsync(); 
     } 

     private static Task DoSomething() 
     { 
      return MyLibrary.DoSomethingAsync(); 
     } 

     private static async Task DoSomethingAsync() 
     { 
      await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 
     } 

     public class MyLibrary 
     { 
      public static async Task DoSomethingAsync() 
      { 
       // Here is some I/O 
      } 
     } 
+0

Try, чтобы избежать использования 'асинхронной main' - см http://stackoverflow.com/questions/9208921/async- on-main-method-of-console-app для получения дополнительной информации. – rhughes

+0

Этот ответ может быть вам полезен: http://stackoverflow.com/questions/22808475/how-to-force-execution-to-stop-till-asynchronous-function-is-fully-executed – user3340627

+0

См. [Любая разница между «Ожидание Task.Run(); return "и" return Task.Run() "?] (http://stackoverflow.com/q/21033150/1768303). – Noseratio

ответ

4

ответ Берина это хорошо, но конкретно не рассматривается конкретный случай в вашем вопросе, который является следующим примером:

1: Возвращение задачи непосредственно

private static Task DoSomething() 
{ 
    return MyLibrary.DoSomethingAsync(); 
} 

2: В ожидании Задача и возвращение результата

private static async Task DoSomethingAsync() 
{ 
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 
} 

В thi с единственной разницей между возвратом Task и ожиданием Task и возвратом ответа является то, что - в последнем случае - конечный автомат должен быть создан инфраструктурой для управления запуском, приостановкой и продолжением метода, ожидающего Task , Это приводит к некоторым издержкам производительности.

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

+0

Вы также должны заметить, что 'async' неявно создает' Task' для вас, в то время как регулярный метод должен вызывать 'Task.StartNew()', если он хочет вернуть сам объект 'Task'. –

+0

@BerinLoritsch только в том случае, если это намерение фактически создать новую задачу - в этом случае задача уже предоставляется «MyLibrary» и может быть просто возвращена. –

+0

Спасибо за ответ. Не следует ли использовать Task.FromResult() вместо Task.StartNew() при возврате в качестве задачи из обычного метода? – coalmee

8

Async/Await все еще относительно новы, но есть некоторые хорошие практики, помогающие прояснить API. Основы являются это:

  • метод объявляя себя как async означает, что он ожидает await позже
  • async неявно создает Task для вас.
  • await походит на закладку. Приложение возобновляется, когда использовалось ключевое слово ожидания.
  • Вы не можете await ничего, что не IAwaitable (чаще всего являются Task) (citation)

В приложении, где есть оба асинхронные вызовы и синхронные вызовы, мы приняли соглашение об именах:

  • async вызывает возврат Task или Task<T> и добавляет слово Async в конец имени.
  • Синхронные вызовы (по умолчанию) просто работают, как и любой метод, и нет специального соглашения.

Часто бывает два метода, которые выполняют одно и то же, но одно синхронно, а другое нет. Вы можете либо реализовать его двумя разными способами, либо перенести один на другой. Это действительно зависит от ваших потребностей и того, что даст вам более гибкое приложение.

В приведенном выше примере правильный способ обработки асинхронных и обычных вызовов метода будет для MyLibrary, чтобы разоблачить два метода. Пример будет выглядеть так:

public static class MyLibrary 
{ 
    public static void DoSomething() 
    { 
     // do some work 
    } 

    public static async Task DoSomethingAsync() 
    { 
     // do similar work but use async API, 
     // can also simply call DoSomething(). 
    } 
} 

// In another part of code would be called like this: 
public static async Task BiggerMethod() 
{ 
    MyLibrary.DoSomething(); 
    await MyLibrary.DoSomethingAsync(); 
} 

То, что вы хотите, чтобы избежать оборачивает в async метод с обычным методом. Как только вы сразу работаете с Task, вы теряете все преимущества async и await, и вы вводите места, где ваш код может блокировать.

+0

ради истины, 'Вы не можете ждать чего-либо, что не является задачей', является неопределенным :) вы можете ждать любой' awaitable', который не должен быть задачей – pkuderov

+0

@pkuderov, спасибо. Это на самом деле то, чего я не знал. –

0

Нет разницы в случае однострочных. Но в случае at least two-async-liners, например:

public static async void Main() 
{ 
    await DoSomething(); 
    await DoSomethingAsync(); 
} 

private static Task DoSomethingTwice() 
{ 
    var do1 = MyLibrary.DoSomethingAsync(); 

    // when you await DoSomethingTwice, next line's reached 
    // when do1 task may not be completed 
    var do2 = MyLibrary.DoSomethingAsync(); 

    // return what??? 
    // how to combine them? 

    // okay, you can do that 
    return Task.WhenAll(do1, do2) 
} 

private static async Task DoSomethingTwiceAsync() 
{ 
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 

    // when you await DoSomethingTwiceAsync, next line's reached 
    // when prev-line task is completed 
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 
} 

public class MyLibrary 
{ 
    public static async Task DoSomethingAsync() 
    { 
     // Here is some I/O 
    } 
} 

Резюме:

  • если вы должны организовать вам метод как цепь последовательного асинхронного мирам работы: doAsync1() ->doAsync2() ->doAsync3(),

т. Е. Каждый следующий мир нуждается в полном результате предыдущего, тогда вы должны дождаться каждого звонка как:

async Task DoAsync() 
{ 
    await doAsync1(); 
    await doAsync2(); 
    await doAsync3(); 
} 
  • но вы не можете использовать ждать в non-async методов. Таким образом, вы не можете сделать это в своем стиле Task DoSomething(), только как async Task DoSomethingAsync().
  • если нет цепи асинхронной - вы можете делать все, что вы хотите, как в моем первом примере выше
+0

Важно отметить, что приведенное выше ('DoAsync') подходит только в том случае, если вам нужны задачи, которые будут выполняться последовательно - если вы хотите, чтобы они были параллельными, вам необходимо отключить их и * затем * ждать их. –

+0

@AntP Спасибо! Это именно то, что я имел в виду как «цепочку асинхронных вызовов». Может быть, я должен сказать «последовательные асинхронные ряды работы». И ваш второй случай в моем первом примере - метод 'DoSomethingTwice'. – pkuderov

Смежные вопросы