async / await方法中的耗时任务

时间:2014-09-06 10:16:43

标签: c# .net multithreading performance async-await

我不太了解与线程相比的async / await好处。

如果在方法内部,我有一个没有async / await版本的操作,在其他异步操作的中间消耗一些时间,比如20ms。

调用此方法1000次,因为async / await仅在一个线程内执行,它将在我的测试中使用至少20ms x 1000次(使用Thread.Sleep进行模拟)。

是否有关于async / await的误解?

public void Start()
{
    Task[] task = new Task[500];

    for (int i = 0; i < 500; i++)
    {
        task[i] = AsyncGet();
    }

    await Task.WhenAll(task);
}
public async Task<bool> AsyncGet()
{
    HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://www.example.com");
    req.Method = "GET";
    using(HttpWebResponse res = (HttpWebResponse)await req.GetResponseAsync())
    {

        Thread.Sleep(20);//Simulate an operation without async/await version that consume a little bit time

        using (Stream responseStream = res.GetResponseStream())
        {
             StreamReader sr = new StreamReader(responseStream, Encoding.ASCII);
             string resp = await sr.ReadToEndAsync();
        }
    }
}

更新问题

如果我需要做一些非异步/等待操作,比如在使用外部库获取http响应后操纵json。什么是避免浪费时间的最佳做法?

3 个答案:

答案 0 :(得分:4)

不,不一定。如果您从单个线程的 SynchronizationContext 调用Start方法(比如说任何UI上下文,如WindowsFormsSynchronizationContext或DispatcherSynchronizationContext),则答案为是[*]。

如果从其他线程(比如工作线程)调用Start。它不会花费 20ms x 1000 。因为在那种情况下,等待后的代码将在ThreadPool thread中执行。因此,在等待之后可以有N个线程执行代码。在你的情况下,N将小于或等于500。

那就是说,你不应该阻止异步方法。如果您需要在异步方法中使用任何同步代码,则应使用await Task.Run(()=> CodeThatBlocks())

* 如果SynchronizationContext.SetSynchronizationContext不覆盖SynchronizationContext

答案 1 :(得分:2)

  

是否有关于async / await的误解?

是的,你似乎有误会。您无法使用阻止呼叫async-await来模拟Thread.Sleep行为。当您await Task.WhenAll时,多个异步请求会同时执行,而忽略底层线程。

如果您想同时卸载多个请求,请使用await Task.Delay对其进行测试,您会注意到行为的变化。

您可以更进一步,在调用ConfigureAwait(false)时使用GetResponseAsync,这样您的方法的继续不会将工作重新编写回原SynchronizationContext(这在处理自定义时很重要诸如WinFormSynchronizationContext之类的并继续在IOCP上执行。

答案 2 :(得分:0)

无论是否有异步/等待结构,休眠线程仍然会在线程中休眠。使用async / await的想法是尽可能保持每个线程都被占用,以避免在等待I / O时创建大量线程。在后台执行CPU密集型操作仍然需要您创建线程。

换句话说,您提供的示例将导致线程冻结20毫秒,无论它是否是async / await的预期用例。

换句话说,要么:

  • 只需使用异步I / O.不要进行任何CPU密集型处理(也不要使用Sleep)。
  • 使用线程池。