Làm cách nào để gọi một phương thức không đồng bộ trong Main?


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

Nếu tôi muốn gọi Go trong phương thức main (), làm cách nào để thực hiện điều đó? Tôi đang thử các tính năng mới của c #, tôi biết tôi có thể kết nối phương thức không đồng bộ với một sự kiện và bằng cách kích hoạt sự kiện đó, phương thức không đồng bộ có thể được gọi.

Nhưng nếu tôi muốn gọi nó trực tiếp trong phương thức chính thì sao? Làm thế nào tôi có thể làm điều đó?

Tôi đã làm một cái gì đó giống như

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


}

Nhưng có vẻ như đó là một khóa chết và không có gì được in trên màn hình.


Có vẻ như tôi đã tìm thấy vấn đề, cos GetAwaiter (). OnCompleted () sẽ trở lại chức năng chính ngay lập tức, vì vậy khi Console.Readkey () được gọi, luồng chính đang chặn do đó không thể in thông báo đầu ra trả về từ tác vụ ra màn hình vì nó đang chờ luồng chính để bỏ chặn. Nếu tôi thay Console.readkey () bằng while (true) {Thread.Sleep (1000); } nó hoạt động tốt.
Larry

Tôi không thể tái tạo vấn đề bế tắc của bạn nhưng, nếu có ai quan tâm..tử sử dụng Go().ConfigureAwait(false).GetAwaiter()để sử dụng ngữ cảnh mới trong phương thức không đồng bộ.
arviman

Câu trả lời:


105

MainPhương pháp của bạn có thể được đơn giản hóa. Đối với C # 7.1 và mới hơn:

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

Đối với các phiên bản trước đó của C #:

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

Đây là một phần vẻ đẹp của asynctừ khóa (và chức năng liên quan): việc sử dụng và tính chất khó hiểu của các lệnh gọi lại được giảm thiểu hoặc loại bỏ đáng kể.


@MarcGravell Tôi đã kết hợp cách tiếp cận mới vào câu trả lời của mình. Câu trả lời của DavidG đưa ra chi tiết hơn một chút, nhưng tôi không còn lạc hậu nữa. 👍
Tim S.

Bạn có thể cần sửa đổi tệp dự án của mình để cho phép sử dụng c # 7.0 trở lên. Câu trả lời được đăng ở đây
Luke

26

Thay vì Wait, tốt hơn là bạn nên sử dụng new test().Go().GetAwaiter().GetResult() vì điều này sẽ tránh các ngoại lệ được đưa vào AggregateExceptions, vì vậy bạn có thể chỉ cần bao quanh phương thức Go () của mình bằng một khối try catch (Exception ex) như bình thường.



Không gọi GetResult () trên một phương thức không đồng bộ. Điều này sẽ chặn luồng chính. Điều này không chỉ đánh bại mục đích của các phương thức không đồng bộ, mà còn để lại chỗ cho bế tắc.
Ruchira

21

Kể từ khi phát hành các async mainphương pháp C # v7.1 đã có sẵn để sử dụng, điều này tránh được nhu cầu về các giải pháp thay thế trong các câu trả lời đã được đăng. Các chữ ký sau đã được thêm vào:

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

Điều này cho phép bạn viết mã của mình như sau:

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

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

2
Tôi đang cố gắng với static async Task Main(string[] args), nhưng tôi gặp lỗi CS5001 Program does not contain a static 'Main' method suitable for an entry point. Tôi đã kiểm tra các thuộc tính của dự án và không có đối tượng khởi động nào có sẵn trong menu thả xuống. Sử dụng bản cập nhật mới nhất của VS2017 + .NET Core 2.0. Làm thế nào để tôi vượt qua điều này?
NightOwl888

@ NightOwl888 Bạn có thấy C # 7.1 trong các thuộc tính của dự án không?
DavidG

4
Vâng, tôi có thể, cảm ơn bạn. Cài đặt mặc định là "phiên bản chính mới nhất", vì vậy cài đặt mặc định là 7.0. Tôi đã thay đổi thành 7.1 và nó biên dịch ngay bây giờ.
NightOwl888

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

1
Câu trả lời này tạo ra một chuỗi nền có thể gây ra sự cố nếu chuỗi nền trước hoàn thành trước
Luke

11

Miễn là bạn đang truy cập đối tượng kết quả từ tác vụ trả về, không cần sử dụng GetAwaiter (Chỉ trong trường hợp bạn đang truy cập kết quả).

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

}

nếu bạn muốn đợi một nhiệm vụ được hoàn thành và thực hiện một số xử lý tiếp theo:

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

Nếu bạn muốn nhận kết quả từ sayHelloAsync và xử lý thêm:

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

Một cách đơn giản cuối cùng để đợi chức năng:

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

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

}

Điều này cần thêm thông tin về phương pháp để lựa chọn và lý do tại sao
Joe Phillips

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

4
Trong khi trả lời, luôn luôn có một cái gì đó chi tiết về lý do tại sao nó là "câu trả lời" cho câu hỏi được hỏi.
Prasoon Karunan V

1

Sử dụng .Wait ()

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

thử thuộc tính "Kết quả"

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

0

C # 9 Các câu lệnh cấp cao nhất đơn giản hóa mọi thứ hơn nữa, bây giờ bạn thậm chí không phải làm gì thêm để gọi asynccác phương thức từ của bạn Main, bạn chỉ có thể làm điều này:

using System;
using System.Threading.Tasks;

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

Để biết thêm thông tin, hãy xem Có gì mới trong C # 9.0, Các câu lệnh cấp cao nhất :

Các câu lệnh cấp cao nhất có thể chứa các biểu thức không đồng bộ. Trong trường hợp đó, điểm nhập tổng hợp trả về a Task, hoặc Task<int>.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.