【问题标题】:What if not await the task?如果不等待任务怎么办?
【发布时间】:2014-05-09 07:22:10
【问题描述】:

这是我的代码:

private static Stopwatch _stopwatch;

static void PrintException(Exception ex)
{
    Console.WriteLine(_stopwatch.Elapsed);
    Console.WriteLine(ex);
}

static void ThrowException1()
{
    throw new InvalidAsynchronousStateException();
}

static void ThrowException2()
{
    throw new NullReferenceException();
}

static async Task ExecuteTask1()
{
    await Task.Delay(1000);
    ThrowException1();
}

static async Task ExecuteTask2()
{
    await Task.Delay(2000);
    ThrowException2();
}

static async Task Execute()
{
    var t1 = ExecuteTask1();
    var t2 = ExecuteTask2();

    try
    {
        await t2;
    }
    catch (NullReferenceException ex)
    {
        // the NullReferenceException will be captured
        Console.WriteLine("==============");
        PrintException(ex);
    }
}

static void Main(string[] args)
{
    TaskScheduler.UnobservedTaskException += (sender, ev) => PrintException(ev.Exception);
    _stopwatch = Stopwatch.StartNew();

    Execute();

    while (true)
    {
        Thread.Sleep(5000);
        GC.Collect();
    }
}

其实我并没有在Execute方法中等待t1,但它似乎仍然被执行,因为我在大约五秒后捕获了AggregateException

谁能告诉我 t1 是什么时候被执行的?就我而言,打印到控制台的异常顺序是 1.NullReferenceException 2.AggregateException

【问题讨论】:

  • 注意:NullReferenceException 是一个保留的异常,因此不应从用户代码中抛出。
  • @SriramSakthivel +1,我只是用于演示目的。

标签: c# async-await


【解决方案1】:

在 async/await 世​​界中,任务是“热的”。因此,当您调用ExecuteTask1 时,返回给您的任务已经在处理中。它已经在那个时候开始了。您可以在ExecuteTask* 的开头添加Console.WriteLine 以查看它们是否立即开始。

await 仅用于(异步)等待任务的完成。它不会启动任务。

我的博客上有一个async intro,您可能会觉得有帮助。

【讨论】:

  • 垃圾收集器是否也跟踪这个?假设您在一个类中执行此操作,属性是否已经被 GC 破坏了,因为它认为没有更多的引用? (如果您不在任何地方返回/存储任务)。
  • @JoelHarkes:在大多数情况下,任务不会被 GC 处理,因为它们是由传递给非托管代码的回调根植的。
  • 哈哈感谢“我在大多数情况下”。好的,因此回调中使用的变量也不会被 GC 处理。谢谢。
  • @JoelHarkes:正确。 Task 在完成之前可以被 GC 的唯一情况是它无论如何都不会完成的情况。 (假设每个人的非托管调用都正确固定了他们需要的内存)。
【解决方案2】:

如果您不await 一个任务仍将执行,您当前的执行上下文将不会“等待它”。

这意味着您无法直接控制任务,如果“任务内部”出现问题,异常不会像使用 awaitt1.Wait() 时那样直接传播到您的执行上下文。

一般来说,在任务中抛出的异常被装箱在 AggregateException 内,因此您不能执行以下操作:

catch (NullReferenceException ex)

您需要执行类似的操作并检查例如对于内部异常:

catch (AggregateException ex)
{
    if(ex.InnerException is NullReferenceException)
        // handle NRE
    else
        throw; // NOT "throw ex" to keep the stack trace
}

【讨论】:

  • 感谢您的回复,但就我而言,如果我只是将catch (NullReferenceException ex) 替换为catch (AggregateException ex),则不会捕获异常。
  • 也可以捕捉泛型的Exception,然后根据当前的类型来判断...
【解决方案3】:

任务在您在这里调用 ExecuteTask1 的位置执行:

var t1 = ExecuteTask1();

您不需要等待任务执行,它无论如何都会运行...如果您希望代码仅在任务完成后恢复执行,则等待任务,否则您的代码将在之后立即继续运行任务已开始运行,无需等待它完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-10
    • 2016-08-06
    • 2017-06-02
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多