C#中的异步主体,并阻止异步Http调用

时间:2019-05-25 18:02:31

标签: c# concurrency .net-core async-await dotnet-httpclient

我对我认为纯粹是异步程序的输出感到困惑。 如您所见,没有明显的反模式(希望如此)和阻止呼叫。

slowURL限制服务器响应10秒钟。我确实通过在10秒钟的超时时间内运行对本地服务器的调用来确认,在控制台中运行代码时,FetchSlowAsync方法调用有效地阻塞了主线程10秒钟。

我希望TaskScheduler不会以顺序的方式安排调用,而是总是随机确定方法的调用顺序。 the,输出始终是确定性的。

FetchSlowAsync start
FetchSlowAsync got data!
FetchAsync start
FetchAsync got data!
FetchBingAsync start
FetchBingAsync got data!
All done!

我的问题是:是什么促使FetchSlowAsync阻止而不是TaskScheduler执行上下文切换到另一个异步方法,并在完成后返回到它?

后面的问题是:async Main中的所有方法为什么在异步执行模型是并发的情况下以与调用它们相同的顺序执行?

using static System.Console;
using System.Net.Http;
using System.Threading.Tasks;

class Start
{
    const string serviceURL = "https://google.com";
    const string slowDown = "http://slowwly.robertomurray.co.uk/delay/10000/url/";
    const string slowURL = slowDown + serviceURL;
    const string OkURL = serviceURL;

    static async Task FetchSlowAsync()
    {
        await Console.Out.WriteLineAsync("FetchSlowAsync start");
        await new HttpClient().GetStringAsync(slowURL); //BLOCKS MAIN THREAD FOR 10 seconds        
        await Console.Out.WriteLineAsync("FetchSlowAsync got data!");
    }

    static async Task FetchAsync()
    {
        await Console.Out.WriteLineAsync("FetchAsync start");
        await new HttpClient().GetStringAsync(OkURL);
        await Console.Out.WriteLineAsync("FetchAsync got data!");        
    }

    static async Task FetchBingAsync()
    {
        await Console.Out.WriteLineAsync("FetchBingAsync start");
        await new HttpClient().GetStringAsync("https://bing.com");
        await Console.Out.WriteLineAsync("FetchBingAsync got data!");
    }

    static async Task Main()
    {
        await FetchSlowAsync();
        await FetchBingAsync();
        await FetchAsync();

        await System.Console.Out.WriteLineAsync("All done!");
    }
}

1 个答案:

答案 0 :(得分:5)

当前,您正在等待FetchSlowAsync()返回的任务完成,然后再继续调用FetchBingAsync等。通过等待任务来完成此任务,这里:

await FetchSlowAsync();
await FetchBingAsync();
await FetchAsync();

如果您不想在启动Bing提取之前等待缓慢的提取完成,等等,您可以记住它们返回的任务,并在以后等待它们:

static async Task Main()
{
    // Start all three tasks
    Task t1 = FetchSlowAsync();
    Task t2 = FetchBingAsync();
    Task t3 = FetchAsync();

    // Then wait for them all to finish
    await Task.WhenAll(t1, t2, t3);

    await Console.Out.WriteLineAsync("All done!");
}

现在,您会得到我认为您期望的那种输出。例如,这是在我的计算机上运行的

FetchSlowAsync start
FetchBingAsync start
FetchAsync start
FetchAsync got data!
FetchBingAsync got data!
FetchSlowAsync got data!
All done!