问题 为什么等待冷任务不会抛出


我只是在尝试看看冷任务时会发生什么(即一个 Task 等待的还没有开始。令我惊讶的是,代码永远挂了 “Finsihed“永远不会被打印出来。我希望会抛出异常。

public async Task Test1()
{
    var task = new Task(() => Thread.Sleep(1000));
    //task.Start();
    await task;
}

void Main()
{
    Test1().Wait();
    Console.WriteLine("Finished");
}

然后我或许可以从另一个线程启动任务,所以我将代码更改为:

public async Task Test1()
{
    var task = new Task(() => Thread.Sleep(1000));
    //task.Start();
    await task;

    Console.WriteLine("Test1 Finished");
}

void Main()
{
    var task1 = Test1();

    Task.Run(() => 
    {
        Task.Delay(5000);   
        task1.Start();
    });

    task1.Wait();
    Console.WriteLine("Finished");
}

但它仍被阻止 task1.Wait()。是否有人知道在等待之后是否有办法开始冷却任务?

否则似乎没有必要 await 一个冷酷的任务,所以也许任务应该在等待时启动或者应该抛出异常。

更新

我正在等待错误的任务,即返回的外部任务 Test1 而不是里面新人。 @Jon Skeet提到的InvalidOperationException被抛入其中 Task.Run 但是因为没有观察到生成的任务,所以主线程上没有抛出异常。放一个 try/catch 内 Task.Run 或者打电话 Wait() 要么 Result 关于返回的任务 Task.Run 在主控制台线程上抛出异常。


8837
2018-06-16 05:46


起源



答案:


您正在尝试启动异步方法返回的任务 -  这不是你开始时的冷酷任务。实际上,如果您为您添加一些诊断 Task.Run 打电话,你会看到抛出异常:

System.InvalidOperationException:可能不会在promise样式的任务上调用Start。

这是一个显示我认为你真正想要做的事情的例子:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Test
{
    static void Main(string[] args)
    {
        // Not using Task.Delay! That would be pointless
        Task t1 = new Task(() => Thread.Sleep(1000));
        Task t2 = Await(t1);
        Console.WriteLine(t2.Status);
        Console.WriteLine("Starting original task");
        t1.Start(); 
        Console.WriteLine(t2.Status);
        t2.Wait();
        Console.WriteLine(t2.Status);        
    }

    static async Task Await(Task task)
    {
        Console.WriteLine("Beginning awaiting");
        await task;
        Console.WriteLine("Finished awaiting");        
    }
}

注意使用 Thread.Sleep 代替 Task.Delay;除非你使用的结果 Task.Delay,它基本上什么都不做。运用 Thread.Sleep 正在模仿要在其他任务中完成的实际工作。

至于等待未开始的任务的原因并没有例外 - 我认为这是合理的,说实话。它允许上述情况有效,在某些情况下可能使生活更轻松。 (例如,您可以在启动它们之前创建许多任务 - 并且您可能希望在启动它们之前开始等待它们完成。)


14
2018-06-16 05:51



谢谢Jon,我想我纠结了一点 - Ned Stoyanov


答案:


您正在尝试启动异步方法返回的任务 -  这不是你开始时的冷酷任务。实际上,如果您为您添加一些诊断 Task.Run 打电话,你会看到抛出异常:

System.InvalidOperationException:可能不会在promise样式的任务上调用Start。

这是一个显示我认为你真正想要做的事情的例子:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Test
{
    static void Main(string[] args)
    {
        // Not using Task.Delay! That would be pointless
        Task t1 = new Task(() => Thread.Sleep(1000));
        Task t2 = Await(t1);
        Console.WriteLine(t2.Status);
        Console.WriteLine("Starting original task");
        t1.Start(); 
        Console.WriteLine(t2.Status);
        t2.Wait();
        Console.WriteLine(t2.Status);        
    }

    static async Task Await(Task task)
    {
        Console.WriteLine("Beginning awaiting");
        await task;
        Console.WriteLine("Finished awaiting");        
    }
}

注意使用 Thread.Sleep 代替 Task.Delay;除非你使用的结果 Task.Delay,它基本上什么都不做。运用 Thread.Sleep 正在模仿要在其他任务中完成的实际工作。

至于等待未开始的任务的原因并没有例外 - 我认为这是合理的,说实话。它允许上述情况有效,在某些情况下可能使生活更轻松。 (例如,您可以在启动它们之前创建许多任务 - 并且您可能希望在启动它们之前开始等待它们完成。)


14
2018-06-16 05:51



谢谢Jon,我想我纠结了一点 - Ned Stoyanov


有没有人知道是否有办法在它之后开始冷任务   等待?

你仍然可以从一个创建冷任务 async 方法并稍后启动它,如果这是你想要的:

class Program
{
    public static async Task Test1()
    {
        await Task.Delay(1000);
        Console.WriteLine("Test1 is about to finish");
    }

    static void Main(string[] args)
    {
        var taskOuter = new Task<Task>(Test1);
        var taskInner = taskOuter.Unwrap();

        Task.Run(() =>
        {
            Thread.Sleep(2000);

            // run synchronously
            taskOuter.RunSynchronously();

            // or schedule
            // taskOuter.Start(TaskScheduler.Defaut);
        });

        taskInner.Wait();
        Console.WriteLine("Enter to exit");
        Console.ReadLine();
    }
}

1
2018-06-16 06:36



谢谢,这清除了它 - Ned Stoyanov