2

我正在使用 TaskFactory 使用以下代码开始新任务。

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

task.Wait();

doAPICall() 的代码如下所示,它调用了一个返回任务的外部 API。

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

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

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

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

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

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

task.Wait();
4

2 回答 2

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.自定义任务调度器不适用于异步代码。您可能不得不采用更异步的调度/并发解决方案。更多信息在我的博客上
  2. 不要阻塞异步代码。我看到两个Waitcall 和 a Result,这两个都是严重的危险信号。更多信息在我的博客上
  3. 不要Task.Status在生产代码中使用。可以进行调试,但您不必将其用于实际逻辑。总有更好的解决方案。更多信息在我的博客上
  4. 只能Task.Run从 UI 层使用,而不是嵌套在帮助程序/库代码中。换句话说,用它来调用方法,而不是实现方法。更多信息在我的博客上

从最里面的方法工作:

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();
于 2016-03-09T13:02:14.140 回答
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)在每次等待之后添加。

于 2016-03-09T07:14:54.840 回答