将异步调用链接在一起 - 调用永远不会返回

时间:2014-03-21 02:30:39

标签: c# .net async-await

我有一个异步调用的层次结构。看起来基本上是这样的:

async MyObject MainWorker()
{
    return await Task.Run(SomeSynchronousFunction);
}

async MyObject InbetweenFunction1()
{
    //do some things
    return await MainWorker();
}

async MyObject InbetweenFunction2()
{
    //do some things 
    return await MainWorker();
}

void ReturnObject TopFunction()
{
    Task<MyObject> return1 = InbetweenFunction1();
    Task<MyObject> return2 = InbetweenFunction2();

    while (!return1.IsComplete || !return2.IsComplete)
         Thread.Sleep(100);

    //access return1.Return and return2.Return values and return from this function
}

所以我有一些级别的异步调用。顶级方法进行两次异步调用,等待它们完成(通过轮询),然后访问它们的返回值并对值进行处理。问题是,没有任何异步调用完成。与Task.Run异步调用的函数SomeSynchronousFunction应该花费几秒钟的时间,但我等待30秒以上没有结果。

这是我第一次尝试使用新的async / await关键字。我做了一件明显不对的事吗?

3 个答案:

答案 0 :(得分:4)

  

我做错了什么?

是的,你正忙着等待(循环直到条件变为true),除非没有其他选择,否则你不应该这样做。此处有一个更好的选择:使用Wait()Task.WaitAll()

但这样做很可能实际上不会使你的代码工作。真正的问题可能是the classic await deadlock:你处于同步上下文(通常是UI线程或ASP.NET请求上下文),而你的await尝试在该上下文中恢复,但是你'还要阻止等待Task s完成的上下文。

解决此问题的正确方法是在await中使用TopLevelFunction()。这将要求您同时创建该函数async,这意味着它的调用者现在也必须是async,等等。这称为“async一直”。

所有async方法都应该是async Task方法,但顶级事件处理程序除外,它们必须是async void。 (但是你不应该在其他任何地方使用async void方法,因为它们不能被await编辑。)

这意味着您的功能将变为:

async Task<ReturnObject> TopFunction()
{
    Task<MyObject> return1 = InbetweenFunction1();
    Task<MyObject> return2 = InbetweenFunction2();

    await Task.WhenAll(return1, return2);

    //access await return1 and await return2 values and return from this function
}

答案 1 :(得分:1)

以下是一些可行的代码:

    private async Task<string> DoMainWork()
    {
        await Task.Delay(3000);
        return "MainWorker";
    }

    private async Task<string> InbetweenFunction1()
    {
        // do something
        await Task.Delay(1000);
        return await DoMainWork() + "1";
    }

    private async Task<string> InbetweenFunction2()
    {
        // do something
        await Task.Delay(2000);
        return await DoMainWork() + "2";
    }

    public string TopFunction()
    {
        string return1 = null;
        string return2 = null;

        var taskList = new List<Task>();
        taskList.Add(Task.Run(async () => return1 = await InbetweenFunction1()));
        taskList.Add(Task.Run(async () => return2 = await InbetweenFunction2()));

        Task.WaitAll(taskList.ToArray());

        return return1 + return2;
    }

答案 2 :(得分:0)

你想要的是一个额外的功能来组合以前的任务

public async Task<string> Combine(Task<string> a, Task<string b){
    var aa = await a;
    var bb = await b;
    return aa + bb; 
}

public string TopFunction()
{
    var task = Combine(InBetweenFunction2(), InVetweenFunction2());

    // Blocking call
    // Really you shouldn't be doing this unless you really really
    // have to.
    task.Wait();

    return task.Result;
}

请注意,really really必须使用阻止调用的其中一个位置位于应用程序的入口点main function,该入口点无法声明为async,因此可以&#39 ; t使用await