cjk*_*k84 15 .net c# task-parallel-library async-await .net-4.5
在试图找出新的(现在可能不是那么新,但对我而言是新的)TaskC#中的异步编程时,我遇到了一个问题,让我有点想弄清楚,我不知道为什么.
我已经解决了这个问题,但我仍然不确定为什么这是一个问题.我只是觉得我会分享我的经验,以防任何人遇到同样的情况.
如果有任何大师想告诉我这个问题的原因,那就太棒了,非常感激.我总是喜欢知道为什么有些东西不起作用!
我做了一个测试任务,如下:
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();
Run Code Online (Sandbox Code Playgroud)
然后我运行应用程序以查看它吐出的内容.这是一些示例输出:
6:06:15.5661 - 启动延迟3053ms的测试任务.
6:06:15.5662 - 等完了.
6:06:18.5743 - 测试任务在3063.235ms之后完成.
如您所见,该Task.WaitAll(tasks);声明没有做太多.在继续执行之前,它总共等待了1毫秒.
我已经在下面回答了我自己的"问题" - 但正如我上面所说 - 如果有更多知识渊博的人会解释为什么这不起作用,请做!(我认为一旦它到达一个await操作员,它可能与该方法的执行'踩出'有关- 然后在等待完成后退回......但我可能错了)
i3a*_*non 19
你应该避免使用Task.Factory.StartNewasync-await.你应该使用Task.Run.
异步方法返回a Task<T>,异步委托也返回.Task.Factory.StartNew也返回a Task<T>,其结果是delegate参数的结果.因此,当一起使用时,它返回一个Task<Task<T>>>.
所有这一切Task<Task<T>>都是执行委托,直到有一个任务要返回,即第一个await到达时.如果您只等待该任务完成,则不等待整个方法,只是等待第一个等待之前的部分.
您可以通过使用Task.Unwrap创建Task<T>表示以下内容的方法来解决此问题Task<Task<T>>>:
Task<Task> wrapperTask = Task.Factory.StartNew(...);
Task actualTask = wrapperTask.Unwrap();
Task.WaitAll(actualTask);
Run Code Online (Sandbox Code Playgroud)
您的代码的问题在于有两个任务在起作用.一个是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();
Run Code Online (Sandbox Code Playgroud)