Task.Run工作原理

时间:2017-03-24 14:47:01

标签: c# multithreading asynchronous async-await task

我想这很简单,但我对任务的工作有一些误解

当我调用下面的代码时,它运行良好,我得到的结果大约1秒。

return Task.Run(() => SendRequest<IADsystem[]>(path)).Result;

但是这个永远不会返回结果:

  Task<IADsystem[]> task = SendRequest<IADsystem[]>(path);
  task.Wait(); //also tried without wait
  return task.Result;

我想也许这个任务没有启动,我需要调用Start(),但是当我创建它时,我得到了“Start可能不会在promise风格的任务上调用”异常。

SendRequest方法:

 private async Task<T> SendRequest<T>(string requestUri) where T : class
    {
        var authHandler = new HttpClientHandler();
        authHandler.Credentials = CredentialCache.DefaultNetworkCredentials;

        using(var client = new HttpClient(authHandler))
        {
            client.BaseAddress = apiServerURI;
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(jsonAcceptType);

            HttpResponseMessage response = await client.GetAsync(requestUri);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsAsync<T>();
            }
            else
                return null;
        }
    }

请解释它是如何运作的

3 个答案:

答案 0 :(得分:1)

首先考虑Wait()方法:

Task<IADsystem[]> task = SendRequest<IADsystem[]>(path);
task.Wait(); //also tried without wait
return task.Result;

此代码使用sync-over-async反模式。这可以是cause a deadlock,正如我在博客中详细描述的那样。核心问题是您的代码阻塞了异步代码。这在某些情况下有效,但在这种情况下,您会看到常见的死锁。

总之,这是由于await捕获当前上下文(通常是SynchronizationContext),并使用它来恢复其async方法。因此,如果从UI线程(或ASP.NET请求上下文)调用此代码,则await内的SendRequest将尝试在该上下文中继续,但上下文被{阻止{1}} / Wait,导致死锁。

请注意,问题是由于同步调用异步方法所致。正如我在async best practices article中描述的那样,理想的解决方案是“一直异步”。

回到实际问题:

  

Task.Run如何运作

     

当我调用下面的代码时,它运作良好

Result

排序。它在“没有死锁”的意义上“有效”。但是,它并不理想,因为它会在请求期间阻塞线程池线程。更好的解决方案是一直使用return Task.Run(() => SendRequest<IADsystem[]>(path)).Result;

但暂时搁置一旁,为什么它起作用的原因是因为它“走出”调用上下文。 async在线程池线程(没有UI或ASP.NET请求上下文的线程)上执行Task.Run,因此SendRequest中的await在线程池上恢复线程。这就是为什么你的调用线程可以在没有死锁的情况下阻塞它。

但它仍然不应该阻止任务;它应该SendRequest代替。

答案 1 :(得分:0)

async / await方法必须始终为async / await。如果您使用的是Windows窗体,那么任何包含调用SendRequest的代码的事件处理程序都必须标记为异步。 UI事件处理程序和SendRequest之间的每个方法也必须标记为异步,并且必须等待它们。你应该

var result = await SendRequest<IADsystem[]>(path);
像法比奥建议的那样。

Here是关于async / await的更多信息。

答案 2 :(得分:0)

我在这个页面开始学习async,所以我认为这是一个很好的起点。我恳请您查看执行上下文Async - Starting Point

为什么你有死锁,我会试着解释一下。当您调用task.Wait()时,您基本上是在告诉当前线程等待结果继续。所以现在你当前的线程或你的Execution Context正在等待结果继续。让我们继续吧。当您调用task.Wait()时,您输入了private async Task<T> SendRequest<T>(string requestUri)...方法,当您到达return await response.Content.ReadAsAsync<T>();时,您已通过当前执行上下文,正在等待SendRequest<T>的结果。现在,你处于死锁状态。为什么?您的UI线程正在等待SendRequest<T>的答案,并且为了SendRequest<T>完成(或return),它需要访问UI线程,但您无法访问UI线程,因为它正在等待SendRequest<T>完成,但要SendRequest<T>完成它需要访问UI线程....