Як я можу викликати метод асинхронізації в Main?


81
public class test
{
    public async Task Go()
    {
        await PrintAnswerToLife();
        Console.WriteLine("done");
    }

    public async Task PrintAnswerToLife()
    {
        int answer = await GetAnswerToLife();
        Console.WriteLine(answer);
    }

    public async Task<int> GetAnswerToLife()
    {
        await Task.Delay(5000);
        int answer = 21 * 2;
        return answer;
    }
}

якщо я хочу викликати метод Go у main (), як я можу це зробити? Я випробовую нові функції c #, я знаю, що можу підключити метод async до події, і, запускаючи цю подію, можна викликати метод async.

Але що, якщо я хочу викликати це безпосередньо в основному методі? Як я можу це зробити?

Я зробив щось подібне

class Program
{
    static void Main(string[] args)
    {
        test t = new test();
        t.Go().GetAwaiter().OnCompleted(() =>
        {
            Console.WriteLine("finished");
        });
        Console.ReadKey();
    }


}

Але, здається, це глухий замок, і на екрані нічого не друкується.


здається, я знайшов проблему, тому що GetAwaiter (). OnCompleted () негайно повернеться до основної функції, тому при виклику Console.Readkey () основний потік блокується, тому вихідне повідомлення, яке повертається із завдання, не можна надрукувати на екрані тому що він чекає розблокування основного потоку. Якщо я заміню Console.readkey () на while (true) {Thread.Sleep (1000); } це чудово працює.
Ларрі

Я не можу відтворити вашу проблему глухого кута, але, якщо когось це цікавить, спробуйте Go().ConfigureAwait(false).GetAwaiter()використати новий контекст у методі async.
Арвіман

Відповіді:


105

Ваш Mainметод можна спростити. Для C # 7.1 та новіших версій:

static async Task Main(string[] args)
{
    test t = new test();
    await t.Go();
    Console.WriteLine("finished");
    Console.ReadKey();
}

Для попередніх версій C #:

static void Main(string[] args)
{
    test t = new test();
    t.Go().Wait();
    Console.WriteLine("finished");
    Console.ReadKey();
}

Це є частиною краси asyncключового слова (та пов'язаної з ним функціональності): використання та заплутаний характер зворотних викликів значно зменшується або усувається.


@MarcGravell Я включив новий підхід у свою відповідь. Відповідь DavidG дає трохи більше деталей, але я вже не застарів. 👍
Тім С.

Можливо, вам доведеться змінити файл проекту, щоб дозволити використовувати c # 7.0 і вище. Відповідь розміщено тут
Лука

26

Замість Wait, вам краще використовувати, new test().Go().GetAwaiter().GetResult() оскільки це дозволить уникнути винятків, які обертаються в AggregateExceptions, тому ви можете просто оточити свій метод Go () блоком try catch (Exception ex), як зазвичай.



Не викликайте GetResult () на асинхронному методі. Це заблокує основний потік. Це не лише перешкоджає призначенню асинхронних методів, але й залишає місце для тупикових ситуацій.
Ручіра,

21

З моменту випуску async mainметодів C # v7.1 стали доступними для використання, що дозволяє уникнути потреби в обхідних шляхах у вже розміщених відповідях. Додано такі підписи:

public static Task Main();
public static Task<int> Main();
public static Task Main(string[] args);
public static Task<int> Main(string[] args);

Це дозволяє писати ваш код так:

static async Task Main(string[] args)
{
    await DoSomethingAsync();
}

static async Task DoSomethingAsync()
{
    //...
}

2
Я намагаюся static async Task Main(string[] args), але я отримую помилку CS5001 Program does not contain a static 'Main' method suitable for an entry point. Я перевірив властивості проекту, і в розкривному меню немає доступного об’єкта запуску. Використовуючи останнє оновлення VS2017 + .NET Core 2.0. Як мені це подолати?
NightOwl888

@ NightOwl888 Чи бачите ви C # 7.1 у властивостях проекту?
DavidG

4
Так, можу, дякую. За замовчуванням встановлено "остання основна версія", тож за замовчуванням воно становило 7,0. Я змінився на 7.1, і він компілюється зараз.
NightOwl888

12
class Program
{
    static void Main(string[] args)
    {
       test t = new test();
       Task.Run(async () => await t.Go());
    }
}

1
Ця відповідь створює фоновий потік, який може спричинити проблеми, якщо потік переднього плану завершиться першим
Лука

11

Поки ви отримуєте доступ до об'єкта результату із поверненого завдання, взагалі немає потреби використовувати GetAwaiter (Тільки у випадку, якщо ви отримуєте доступ до результату).

static async Task<String> sayHelloAsync(){

       await Task.Delay(1000);
       return "hello world";

}

static void main(string[] args){

      var data = sayHelloAsync();
      //implicitly waits for the result and makes synchronous call. 
      //no need for Console.ReadKey()
      Console.Write(data.Result);
      //synchronous call .. same as previous one
      Console.Write(sayHelloAsync().GetAwaiter().GetResult());

}

якщо ви хочете дочекатися завершення завдання та виконати подальшу обробку:

sayHelloAsyn().GetAwaiter().OnCompleted(() => {
   Console.Write("done" );
});
Console.ReadLine();

Якщо ви зацікавлені отримати результати від sayHelloAsync і виконайте подальшу обробку на ньому:

sayHelloAsync().ContinueWith(prev => {
   //prev.Result should have "hello world"
   Console.Write("done do further processing here .. here is the result from sayHelloAsync" + prev.Result);
});
Console.ReadLine();

Останній простий спосіб очікування на функцію:

static void main(string[] args){
  sayHelloAsync().Wait();
  Console.Read();
}

static async Task sayHelloAsync(){          
  await Task.Delay(1000);
  Console.Write( "hello world");

}

Тут потрібна додаткова інформація про те, який метод вибрати і чому
Джо Філліпс

5
public static void Main(string[] args)
{
    var t = new test();
    Task.Run(async () => { await t.Go();}).Wait();
}

4
Відповідаючи, завжди майте щось докладно про те, чому це «відповідь» на поставлене запитання.
Прасун Карунан V

1

Використовуйте .Зачекайте ()

static void Main(string[] args){
   SomeTaskManager someTaskManager  = new SomeTaskManager();
   Task<List<String>> task = Task.Run(() => marginaleNotesGenerationTask.Execute());
   task.Wait();
   List<String> r = task.Result;
} 

public class SomeTaskManager
{
    public async Task<List<String>> Execute() {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri("http://localhost:4000/");     
        client.DefaultRequestHeaders.Accept.Clear();           
        HttpContent httpContent = new StringContent(jsonEnvellope, Encoding.UTF8, "application/json");
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        HttpResponseMessage httpResponse = await client.PostAsync("", httpContent);
        if (httpResponse.Content != null)
        {
            string responseContent = await httpResponse.Content.ReadAsStringAsync();
            dynamic answer = JsonConvert.DeserializeObject(responseContent);
            summaries = answer[0].ToObject<List<String>>();
        }
    } 
}

0

спробуйте властивість "Результат"

class Program
{
    static void Main(string[] args)
    {
        test t = new test();
        t.Go().Result;
        Console.ReadKey();
    }
}

0

Заяви верхнього рівня C # 9 ще більше спростили речі, тепер вам навіть не потрібно робити нічого зайвого для виклику asyncметодів з вашого Main, ви можете просто зробити це:

using System;
using System.Threading.Tasks;

await Task.Delay(1000);
Console.WriteLine("Hello World!");

Для отримання додаткової інформації дивіться Що нового в C # 9.0, Заяви верхнього рівня :

Оператори верхнього рівня можуть містити асинхронні вирази. У цьому випадку синтезована точка входу повертає a Task, або Task<int>.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.