无法理解C#中async、await的行为

3
我正在学习C#的await async特性。但是下面代码的行为让我感到困惑。
public static async Task CreateMultipleTasksAsync() {
    HttpClient client = new HttpClient() {
        MaxResponseContentBufferSize = 1000000
    };
    Task <int> download1 = ProcessUrlAsync("https://msdn.microsoft.com", client);
    Task <int> download2 = ProcessUrlAsync("https://msdn.microsoft.com/library/67w7t67f.aspx", client);
    int length1 = await download1;
    int length2 = await download2;
    Console.WriteLine("Sum is {0}", length1 + length2);
}

public static async Task <int> ProcessUrlAsync(string url, HttpClient client) {
    Console.WriteLine("I am here to process {0}", url);
    var byteArray = await client.GetByteArrayAsync(url);
    Console.WriteLine("processing is completed {0}", url);

    return byteArray.Length;
}

完成download1和download2后,我希望CreateMultipleAsync()的最后一行将被执行,并打印长度总和。问题在于最后一行从未执行!

1
你是怎么调用那个方法的?你看到其他的消息了吗? - Hans Kesting
我只看到ProcessUrlAsync方法中的第一条消息。 我从另一个方法中调用CreateMultipleTasksAsync()方法,该方法只是等待它,名为StartMultiple(),并从构造函数中调用该方法。 - Noymul Islam Chowdhury
如果您提供一个最小可复现示例 [mcve],那将非常有帮助。(顺便说一下,当我运行这段代码时,第二个URL会失败:System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel) - Jon Skeet
这是我的整个代码:https://paste.ubuntu.com/p/rBccHq4HXD/ - Noymul Islam Chowdhury
2
嗯,那真的不是如何使用构造函数。这就是整个问题所在。您需要一个异步入口点,并且构造函数必须是同步的。而且,老实说,您不应该在构造函数中执行那样的重型操作。 - John Wu
2个回答

2

这是因为您在构造函数中使用了异步方法。请将其从此处移除。

另外,请不要使用async void。而应该使用async Task

最初的回答

class MultipleAsync
{
    public async Task StartMultiple()
    {
        await CreateMultipleTasksAsync();

    }
    public static async Task CreateMultipleTasksAsync()
    {
        HttpClient client = new HttpClient() { MaxResponseContentBufferSize = 1000000 };
        Task<int> download1 = ProcessUrlAsync("https://msdn.microsoft.com", client);
        Task<int> download2 = ProcessUrlAsync("https://msdn.microsoft.com/library/67w7t67f.aspx", client);
        int length1 = await download1;
        int length2 = await download2;
        Console.WriteLine("Sum is {0}", length1 + length2);
    }
    public static async Task<int> ProcessUrlAsync(string url, HttpClient client)
    {
        Console.WriteLine("I am here to process {0}", url);
        var byteArray = await client.GetByteArrayAsync(url);
        Console.WriteLine("processing is completed {0}", url);

        return byteArray.Length;

    }
}

使用方法:

MultipleAsync multipleAsync = new MultipleAsync();
await multipleAsync.StartMultiple();

另外,由于你的第二个任务不依赖于第一个任务的结果,所以你可以并行运行两个任务。第二个任务不会等待第一个任务完成:

原始答案翻译成中文为“最初的回答”。

HttpClient client = new HttpClient() { MaxResponseContentBufferSize = 1000000 };
Task<int> download1 = ProcessUrlAsync("https://msdn.microsoft.com", client);
Task<int> download2 = ProcessUrlAsync("https://msdn.microsoft.com/library/67w7t67f.aspx", client);
var lengthts = await Task.WhenAll(download1, download2);
Console.WriteLine("Sum is {0}", lengthts.Sum());

2
我认为问题在于您执行CreateMultipleTasksAsync()方法的方式。
如果您从同步方法中执行它,就像这样:
static void Main(string[] args)
    {
        CreateMultipleTasksAsync();
    }

那么你将无法看到最后一行的结果。
为什么?因为此调用未被等待,所以Main方法将继续执行并结束,而在此之前CreateMultipleTasksAsync()方法未被完成。因此,您将无法看到最后一行的结果。
按照以下方式执行,您就可以看到它了

static void Main(string[] args)
    {
        var task = CreateMultipleTasksAsync();
        task.Wait();
    }

最初的回答
祝你好运。

可以,这对我有用!但是当我将代码更改为https://paste.ubuntu.com/p/VRHkMF5j5w/时,也可以工作!您能帮助我理解await和直接获取GetAwaiter()。GetResult()之间的区别吗? - Noymul Islam Chowdhury
抱歉让您久等了。在方法 CreateMultipleTasksAsync() 中,await 将暂停其进程并将控制权移交给调用 CreateMultipleTasksAsync() 的方法。而 GetAwaiter().GetResult() 将等待 ProcessUrlAsync() 完成。如果您使用异步而没有使用 await,则您的方法实际上不是异步方法。阅读此图表和下面的步骤以更好地理解 await 链接 - dunghxh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接