Task.WaitAll不等待任务完成

时间:2015-08-22 12:35:15

标签: c# .net async-await task-parallel-library .net-4.5

在试图找出新的(现在可能不是那么新,但对我来说是新的){CES中的Task Random rng = new Random((int)DateTime.UtcNow.Ticks); int delay = rng.Next(1500, 15000); Task<Task<object>> testTask = Task.Factory.StartNew<Task<object>>( async (obj) => { DateTime startTime = DateTime.Now; Console.WriteLine("{0} - Starting test task with delay of {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (int)obj); await Task.Delay((int)obj); Console.WriteLine("{0} - Test task finished after {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (DateTime.Now - startTime).TotalMilliseconds); return obj; }, delay ); Task<Task<object>>[] tasks = new Task<Task<object>>[] { testTask }; Task.WaitAll(tasks); Console.WriteLine("{0} - Finished waiting.", DateTime.Now.ToString("h:mm:ss.ffff")); // make console stay open till user presses enter Console.ReadLine(); 异步编程时,我遇到了一个让我想一想的问题,我和#39;我不确定为什么。

我已经解决了这个问题,但我仍然不确定为什么这是一个问题。我以为我会分享我的经验,以防任何人遇到同样的情况。

如果有任何大师想告诉我这个问题的原因,那就太棒了,非常感激。我总是喜欢知道为什么某些东西不起作用!

我做了一个测试任务,如下:

Task.WaitAll(tasks);

然后我运行应用程序以查看它吐出的内容。以下是一些示例输出:

  

6:06:15.5661 - 开始测试任务,延迟3053ms   6:06:15.5662 - 完成等待   6:06:18.5743 - 测试任务在3063.235ms之后完成。

正如您所看到的,await声明并没有做多少。在继续执行之前,它总共等待了1毫秒。

我已回答了我自己的问题&#34;下面 - 但正如我上面所说的 - 如果有人比我更有知识可以解释为什么这不起作用,请做! (我认为一旦它到达System.Threading.Thread.Sleep运营商,它可能与该方法的执行和踩踏有关 - 然后在等待时退回完了...但我可能错了)

4 个答案:

答案 0 :(得分:19)

您应该避免将Task.Factory.StartNew与async-await一起使用。您应该使用Task.Run代替。

异步方法返回Task<T>,异步委托也会返回。 Task.Factory.StartNew也返回Task<T>,其结果是delegate参数的结果。因此,当一起使用时,它会返回Task<Task<T>>>

这个Task<Task<T>>执行的所有操作都是执行委托,直到有一个要返回的任务,即到达第一个await时。如果你只是等待那个任务完成,你就不会等待整个方法,只是等待第一个方法之前的部分。

您可以使用Task.Unwrap创建表示Task<T>的{​​{1}}来解决此问题:

Task<Task<T>>>

答案 1 :(得分:9)

您的代码存在的问题是有两个任务在起作用。一个是Task.Factory.StartNew调用的结果,这会导致在线程池上执行匿名函数。但是,您的匿名函数将被编译为生成嵌套任务,表示其异步操作的完成。当您等待Task<Task<object>>时,您只需等待外部任务。要等待内部任务,您应该使用Task.Run而不是Task.Factory.StartNew,因为它会自动解开您的内部任务:

Random rng = new Random((int)DateTime.UtcNow.Ticks);
int delay = rng.Next(1500, 15000);
Task<int> testTask = Task.Run(
    async () =>
    {
        DateTime startTime = DateTime.Now;
        Console.WriteLine("{0} - Starting test task with delay of {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), delay);
        await Task.Delay(delay);
        Console.WriteLine("{0} - Test task finished after {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (DateTime.Now - startTime).TotalMilliseconds);
        return delay;
    });
Task<int>[] tasks = new[] { testTask };

Task.WaitAll(tasks);
Console.WriteLine("{0} - Finished waiting.", DateTime.Now.ToString("h:mm:ss.ffff"));

// make console stay open till user presses enter
Console.ReadLine();

答案 2 :(得分:3)

这里,Task.WaitAll等待外部任务,而不是内部任务。使用Task.Run没有嵌套任务。这是最佳实践解决方案。另一个解决方案是等待内部任务。例如:

Task<object> testTask = Task.Factory.StartNew(
    async (obj) =>
        {
            ...
        }
    ).Unwrap();

或者:

testTask.Wait();
testTask.Result.Wait();

答案 3 :(得分:0)

经过多次拧紧和拔毛之后,我终于决定摆脱异步lambda并使用Random rng = new Random((int)DateTime.UtcNow.Ticks); int delay = rng.Next(1500, 15000); Task<object> testTask = Task.Factory.StartNew<object>( (obj) => { DateTime startTime = DateTime.Now; Console.WriteLine("{0} - Starting test task with delay of {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (int)obj); System.Threading.Thread.Sleep((int)obj); Console.WriteLine("{0} - Test task finished after {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (DateTime.Now - startTime).TotalMilliseconds); return obj; }, delay ); Task<object>[] tasks = new Task<object>[] { testTask }; Task.WaitAll(tasks); Console.WriteLine("{0} - Finished waiting.", DateTime.Now.ToString("h:mm:ss.ffff")); // make console stay open till user presses enter Console.ReadLine(); 方法,看看是否有任何区别。

新代码最终结果如下:

async

注意:由于从lambda方法中删除了Task<object>关键字,因此任务类型现在可以只是Task<Task<object>>而不是System.Threading.Thread.Sleep() - 您可以在上面的代码中看到此更改。

而且,瞧!有效!我完成了等待。&#39;任务完成后的消息。

唉,我记得在某个地方读过你不应该在你的Task代码中使用oWidth.setType(STTblWidth.PCT); 。不记得为什么;但是因为它只是用于测试而且大多数任务实际上都是做一些花费时间的事情而不是假装做一些需要时间的事情,这不应该是真的是一个问题。

希望这可以帮助一些人。我绝对不是世界上最好的程序员(甚至是关闭的),而且我的代码可能不是很好,但如果它对某人有帮助,那太好了! :)

感谢阅读。

  

编辑:我的问题的其他答案解释了为什么我遇到了问题,这个答案只是错误地解决了问题。更改为Thread.Sleep(x)无效。感谢所有回答并帮助我的人!