简体   繁体   English

如何声明将等待另一个任务的未启动任务?

[英]How to declare a not started Task that will Await for another Task?

I've done this Unit Test and I don't understand why the "await Task.Delay()" doesn't wait !我已经完成了这个单元测试,但我不明白为什么“await Task.Delay()”不等待!

   [TestMethod]
    public async Task SimpleTest()
    {
        bool isOK = false;
        Task myTask = new Task(async () =>
        {
            Console.WriteLine("Task.BeforeDelay");
            await Task.Delay(1000);
            Console.WriteLine("Task.AfterDelay");
            isOK = true;
            Console.WriteLine("Task.Ended");
        });
        Console.WriteLine("Main.BeforeStart");
        myTask.Start();
        Console.WriteLine("Main.AfterStart");
        await myTask;
        Console.WriteLine("Main.AfterAwait");
        Assert.IsTrue(isOK, "OK");
    }

Here is the Unit Test output:这是单元测试 output:

单元测试输出

How is this possible an "await" doesn't wait, and the main thread continues?这怎么可能“等待”不等待,而主线程继续?

new Task(async () =>

A task does not take a Func<Task> , but an Action .任务不采用Func<Task> ,而是采用Action It will call your asynchronous method and expect it to end when it returns.它将调用您的异步方法并期望它在返回时结束。 But it does not.但事实并非如此。 It returns a task.它返回一个任务。 That task is not awaited by the new task.新任务不等待该任务。 For the new task, the job is done once the method returned.对于新任务,一旦方法返回,工作就完成了。

You need to use the task that already exists instead of wrapping it in a new task:您需要使用已经存在的任务,而不是将其包装在新任务中:

[TestMethod]
public async Task SimpleTest()
{
    bool isOK = false;

    Func<Task> asyncMethod = async () =>
    {
        Console.WriteLine("Task.BeforeDelay");
        await Task.Delay(1000);
        Console.WriteLine("Task.AfterDelay");
        isOK = true;
        Console.WriteLine("Task.Ended");
    };

    Console.WriteLine("Main.BeforeStart");
    Task myTask = asyncMethod();

    Console.WriteLine("Main.AfterStart");

    await myTask;
    Console.WriteLine("Main.AfterAwait");
    Assert.IsTrue(isOK, "OK");
}

The problem is that you are using the non-generic Task class, that is not meant to produce a result.问题是您使用的是非通用Task class,这并不意味着产生结果。 So when you create the Task instance passing an async delegate:因此,当您创建传递异步委托的Task实例时:

Task myTask = new Task(async () =>

...the delegate is treated as async void . ...委托被视为async void An async void is not a Task , it cannot be awaited, its exception cannot be handled, and it's a source of thousands of questions made by frustrated programmers here in StackOverflow and elsewhere.一个async void不是一个Task ,它不能被等待,它的异常不能被处理,它是 StackOverflow 和其他地方的受挫程序员提出的成千上万个问题的来源。 The solution is to use the generic Task<TResult> class, because you want to return a result, and the result is another Task .解决方法是使用通用的Task<TResult> class,因为你要返回一个结果,结果是另一个Task So you have to create a Task<Task> :所以你必须创建一个Task<Task>

Task<Task> myTask = new Task<Task>(async () =>

Now when you Start the outer Task<Task> it will be completed almost instantly because its job is just to create the inner Task .现在,当您Start外部Task<Task>时,它将几乎立即完成,因为它的工作只是创建内部Task You'll then have to await the inner Task as well.然后,您还必须等待内部Task This is how it can be done:这是可以做到的:

myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;

You have two alternatives.你有两个选择。 If you don't need an explicit reference to the inner Task then you can just await the outer Task<Task> twice:如果您不需要对内部Task的显式引用,那么您只需等待外部Task<Task>两次:

await await myTask;

...or you can use the built-in extension method Unwrap that combines the outer and the inner tasks into one: ...或者您可以使用内置的扩展方法Unwrap将外部和内部任务组合为一个:

await myTask.Unwrap();

This unwrapping happens automatically when you use the much more popular Task.Run method that creates hot tasks, so the Unwrap is not used very often nowadays.当您使用更流行的创建热任务的Task.Run方法时,这种展开会自动发生,因此现在不经常使用Unwrap

In case you decide that your async delegate must return a result, for example a string , then you should declare the myTask variable to be of type Task<Task<string>> .如果您决定异步委托必须返回结果,例如string ,则应将myTask变量声明为Task<Task<string>>类型。

Note: I don't endorse the use of Task constructors for creating cold tasks.注意:我不赞成使用Task构造函数来创建冷任务。 As a practice is generally frowned upon, for reasons I don't really know, but probably because it is used so rarely that it has the potential of catching other unaware users/maintainers/reviewers of the code by surprise.作为一种实践,通常不受欢迎,原因我不太清楚,但可能是因为它很少使用,以至于它有可能让其他不知情的用户/维护者/审阅者措手不及。

General advice: Be careful everytime you are supplying an async delegate as an argument to a method.一般建议:每次提供异步委托作为方法的参数时都要小心。 This method should ideally expect a Func<Task> argument (meaning that understands async delegates), or at least a Func<T> argument (meaning that at least the generated Task will not be ignored).理想情况下,此方法应该期望一个Func<Task>参数(意味着理解异步委托),或至少一个Func<T>参数(意味着至少生成的Task不会被忽略)。 In the unfortunate case that this method accepts an Action , your delegate is going to be treated as async void .在此方法接受Action的不幸情况下,您的委托将被视为async void This is rarely what you want, if ever.这很少是你想要的,如果有的话。

 [Fact]
        public async Task SimpleTest()
        {
            bool isOK = false;
            Task myTask = new Task(() =>
            {
                Console.WriteLine("Task.BeforeDelay");
                Task.Delay(3000).Wait();
                Console.WriteLine("Task.AfterDelay");
                isOK = true;
                Console.WriteLine("Task.Ended");
            });
            Console.WriteLine("Main.BeforeStart");
            myTask.Start();
            Console.WriteLine("Main.AfterStart");
            await myTask;
            Console.WriteLine("Main.AfterAwait");
            Assert.True(isOK, "OK");
        }

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM