TaskFactory内部任务永远不会执行并始终处于WaitingForActivation状态

时间:2016-03-09 06:19:52

标签: c# .net task-parallel-library task taskfactory

我使用TaskFactory使用以下代码启动新任务。

var task = Task.Factory.StartNew(async () =>
{
    await Task.Run(() =>
    {
        // Do API Call
        var saveResponse = doAPICall();
    }).ConfigureAwait(false);
}).Unwrap();

task.Wait();

doAPICall()的代码如下所示,它调用返回Task的外部API。

private string doAPICall()
{
    Task<string> response = client.FindBags(request);
    response.Wait(60000);

    if (response.Status == TaskStatus.RanToCompletion)
    {
        return response.Result;
    }
}

问题出现在doAPICall()函数中,任务&#39;响应&#39;状态永远不会更改为RanToCompletion,它始终处于WaitingForActivation状态。我已经尝试增加等待超时但仍然没有运气。我正在使用TaskFactory而不是Task,因为将来我想创建自定义TaskFactory以更好地控制调度程序和并发。

我的代码中是否缺少内部任务永远不会被执行的东西?

修改 我根据删除不必要的线程的注释修改了调用doAPICall()的代码,但仍然没有运气。 : - (

var task = Task.Factory.StartNew(() =>
{
    // Do API Call
        var saveResponse = doAPICall();
});

task.Wait();

2 个答案:

答案 0 :(得分:3)

您目前正在使用sync-over-fake-async-over-fake-async-over-sync-over-async。这是非常混乱的。

请遵循以下准则:

  1. 不要使用Task.Factory.StartNew。永远。 I am using the TaskFactory instead of Task because of in future I want to create custom TaskFactory for more control over scheduler and concurrency.自定义任务调度程序不能很好地处理异步代码。您可能不得不采用更多异步友好的解决方案来进行调度/并发。更多信息on my blog
  2. 不要阻止异步代码。我看到两个Wait个调用和一个Result,这两个调用都是严重的危险信号。更多信息on my blog
  3. 不要在生产代码中使用Task.Status。它可以用于调试,但您不应该将它用于实际逻辑。总有一个更好的解决方案。更多信息on my blog
  4. 仅使用UI图层中的Task.Run,而不是嵌套在帮助程序/库代码中。换句话说,使用它来调用方法,而不是实现方法。更多信息on my blog
  5. 从最里面的方法开始:

    private async Task<string> doAPICallAsync()
    {
      Task<string> responseTask = client.FindBags(request);
      // Note: it would be far better to use a cancellation token here instead of a "timed wait".
      Task timeoutTask = Task.Delay(60000);
      Task completedTask = await Task.WhenAny(responseTask, timeoutTask);
      if (completedTask == responseTask)
        return await completedTask;
    }
    

    ,您的主叫代码变为:

    var saveResponse = await doAPICallAsync();
    

答案 1 :(得分:0)

您正在同步调用中包装异步调用,然后在两个异步调用中。这是很多方面,只需使用一个异步调用,你就可以了。

private Task<string> doAPICallAsync()
{
    return client.FindBags(request);
}

如果您需要在client.FindBags(request)使用此版本后进行一些工作:

private async Task<string> doAPICallAsync()
{
    var result = await client.FindBags(request);
    var someNewResult = //do something with result
    return someNewResult;
}

然后这样称呼:

var result = await doAPICallAsync();

如果你真的需要同步版

private string doAPICall()
{
    return client.FindBags(request).Result;
}

并称之为:

var result = doAPICall();

如果可以,请不要忘记在每次等待后添加ConfigureAwait(false)