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

我已经完成了单元测试,但我不明白为什么“ 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");
    }

这是单元测试的输出:

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

这怎么可能使“等待”不等待,而主线程继续?

h00309110 回答:如何声明一个未启动的任务,将等待另一个任务?

new Task(async () =>

任务不需要Func<Task>,而是Action。它将调用您的异步方法,并期望它在返回时结束。但事实并非如此。它返回一个任务。新任务不会等待该任务。对于新任务,一旦方法返回,作业就完成了。

您需要使用已经存在的任务,而不是将其包装在新任务中:

[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");
}
,

问题是您使用的是非泛型Task类,但这并不是要产生结果。因此,当您创建传递异步委托的Task实例时:

Task myTask = new Task(async () =>

...该委托被视为async voidasync void不是Task,无法等待,无法处理其异常,它是沮丧的程序员在StackOverflow和其他地方编写的thousands of questions的来源。解决方案是使用通用的Task<TResult>类,因为您想返回一个结果,而该结果是另一个Task。因此,您必须创建一个Task<Task>

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

现在,当您StartTask<Task>时,它将几乎立即完成,因为它的工作只是创建内部Task。然后,您还必须等待内部Task。这是可以做到的:

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

您有两种选择。如果不需要显式引用内部Task,则只需等待外部Task<Task>两次:

await await myTask;

...或者您可以使用内置的扩展方法Unwrap将内部和内部任务组合为一个:

await myTask.Unwrap();

当您使用流行度更高的创建热门任务的Task.Run方法时,这种解包会自动发生,因此,Unwrap如今已很少使用。

如果您决定异步委托必须返回结果,例如string,则应将myTask变量声明为Task<Task<string>>类型。

注意:我不赞成使用Task构造函数来创建冷任务。出于种种原因,我通常不赞成这种做法,原因我并不十分了解,但可能是因为它很少使用,以至于有可能使其他未意识到的用户/维护者/代码审查者感到惊讶。

一般建议:每次提供异步委托作为方法的参数时,请务必小心。理想情况下,此方法应使用Func<Task>参数(表示理解异步委托),或至少使用Func<T>参数(表示至少忽略生成的Task)。在不幸的情况下,此方法接受Action,您的委托将被视为async void。如果有的话,这几乎不是您想要的。

,
 [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");
        }

enter image description here

本文链接:https://www.f2er.com/3096803.html

大家都在问