异步和解包对于 StartNew() 是必需的吗?



我有这个代码:

var task = Task.Factory.StartNew(() => service.StartAsync(ct), ct);

但我想知道它是否应该是这样的:

var task = Task.Factory.StartNew(async () => await service.StartAsync(ct), ct).Unwrap();

启动异步服务的第一个是否正确?还是第二个更好?

考虑返回的任务类型,第一个产生Task<Task<int>>,而第二个产生Task<int>。因此,实际上第一个是表示内部任务启动的Task,而第二个解包表示表示服务启动的内部方法返回的Task。最后,您还可以Unwrap第一个并获得相同的效果,而无需async/await,这在这里是不必要的。在这种情况下,这些都没有真正涵盖对StartNew的需求,只是查看您查看的返回类型。

请考虑以下代码:

public class AsyncTesting
{
public void StartServiceTest()
{
Task<Task<int>> tsk1 = Task.Factory.StartNew(() => StartAsync());
Task<int> tsk2 = Task.Factory.StartNew(() => StartAsync()).Unwrap();
Task<int> tsk3 = Task.Factory.StartNew(async () => await StartAsync()).Unwrap();
}
public Task<int> StartAsync() => Task.Delay(2500).ContinueWith(tsk => 1);
}

Unwrap的方法返回一个Task,该表示启动内部Task而不是它所做的工作。

正如JSteward在他们的回答中所解释的那样,第一行代码是错误的。它不会执行您希望它执行的操作:

Task<Task> task = Task.Factory.StartNew(() => service.StartAsync(ct), ct); // Buggy

第二行具有正确的行为,但不是因为异步/等待。可以安全地省略异步/等待。使它正确的是Unwrap。不过,它仍然存在问题,因为它违反了关于不通过TaskScheduler就不创建任务的准则 CA2008 .

解决问题的最佳方法是使用Task.Run方法:

Task task = Task.Run(() => service.StartAsync(ct), ct); // Correct

您可以在Stephen Toub的这篇文章中阅读有关Task.RunTask.Factory.StartNew之间的差异。

最新更新