2017-05-31 204 views
1
static void Main(string[] args) 
{ 
    Action myAction = async() => 
    { 
     await Task.Delay(5); 
     Console.WriteLine(Interlocked.Add(ref ExecutionCounter, 1));     
    }; 

    var actions = new[] { myAction, myAction, myAction }; 

    Task.WaitAll(actions.Select(a => Execute(a)).ToArray()); //This blocks, right? 

    Console.WriteLine("Done waiting on tasks."); 
    Console.ReadLine(); 
} 

static int ExecutionCounter = 0; 

private static Task Execute(Action a) 
{ 
    return Task.Factory.StartNew(async() => 
    { 
     await Task.Delay(5); 
     a(); 
    }); 
} 

这似乎很简单,但当然是输出总是看起来像这样(数字变化的顺序,当然):如何等待异步任务

完成等待任务。

缺少什么我在这里?为什么Task.WaitAll像我期待的那样阻止?

+0

我没有答案,只是一个想法。而不是在等待中调用execute来执行,如果你在1行开始执行任务,那么在之后调用wait all?这是这里的例子:https://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx –

+2

@ chris-crush-code这不会改变任何东西。 – Servy

回答

9

因此,这里有几个单独的错误。

首先,对于Execute,您使用StartNewasync lambda。由于StartNew没有Task<Task>返回过载,像Task.Run呢,你有一个返回Task的方法表明当异步操作完成开始,而不是当异步操作完成,这意味着Task返回通过Execute将基本上马上完成,而不是在Delay完成或您称之为完成的动作之后。另外,在运行异步方法时根本没有理由使用StartNewRun,您可以正常执行它们而不将它们推送到线程池线程。

接下来,Execute接受Action,这意味着它是一种不计算任何值的同步方法。你提供的是异步方法,但由于代理不返回Task,Execute不能await它。如果你想要Execute来处理异步方法,它需要接受一个返回Task的委托。

因此给定所有Execute应该看起来像这样。

private static async Task Execute(Func<Task> action) 
{ 
    await Task.Delay(TimeSpan.FromMilliseconds(5)); 
    await action(); 
} 

接下来进入Main方法。如前所述Execute在您尝试提供async方法时正在接受Action。这意味着当动作运行时,代码将在动作完成之前继续执行。您需要将其调整为使用Task返回方法。

毕竟,你的代码仍然有一个竞争条件,在概念层面上,这将阻止你从理论上得到正确的顺序结果。您可以并行执行3个不同的操作,因此可以按任意顺序完成。当你原子地增加计数器时,可以让一个线程递增计数器,然后再运行另一个线程,递增计数器,打印其值,然后让另一个线程再次运行并打印出该值,给出可能的输出即使修复了上面提到的所有错误,你也拥有了什么。为确保这些值按顺序打印,您需要确保增量和控制台写入是以原子方式执行的。现在

你可以写你的Main方法,像这样:

int ExecutionCounter = 0; 
object key = new object(); 
Func<Task> myAction = async() => 
{ 
    await Task.Delay(TimeSpan.FromMilliseconds(5)); 
    lock (key) 
    { 
     Console.WriteLine(++ExecutionCounter); 
    } 
}; 

var actions = new[] { myAction, myAction, myAction }; 

Task.WaitAll(actions.Select(a => Execute(a)).ToArray()); //This blocks, right? 

是的,正如你提到的评论,称WaitAll将阻止,而不是异步的。

+0

非常好。谢谢。 我意识到竞赛状况,但这对我来说不是一个难题。其余的是。感谢澄清。 – mcwyrm

+0

传递给'StartNew'的lambda不是'async void'('Action');它是'async Task'('Func ')。但是,自从'StartNew'处理'Func ''就像其他任何'Func '一样,它确实有类似的“早期完成”问题,并返回“任务”。 –

+0

@StephenCleary权利,修复。跟随它的错误混淆了我自己,并没有把时间花费在我身上。 – Servy