【问题标题】:Interrupt calling thread when exception in child task子任务异常时中断调用线程
【发布时间】:2021-05-01 17:44:38
【问题描述】:

我有以下场景:

我有一个运行自动化脚本的插件类(不幸的是,它是同步的,这是我无法控制的)。启动时,会启动一个任务,该任务在后台连续运行,直到用户停止它。在此任务运行时,它正在解析来自串行端口的流式传入数据并不断更新响应列表。同时,脚本将更改一些其他值并监控响应的反应。

我的问题是 - 解析器可能会抛出异常并导致任何剩余脚本无效。但是,如果这个并行任务抛出,我找不到中断脚本的好方法(即下面的 void Main())。如果我等到我的脚本完成,然后等待任务,我会得到异常,但到那时可能会浪费很多时间。因此,我正在寻找更好的方法来在并行任务错误时中断正在运行的脚本。我尝试在初始同步上下文上发布,但也没有运气。我已经在下面编写了一个示例来模拟类似的场景

public class Test : TestPlugin
{
    Task _t;
    List<string> data = new List();

    public Test(){ }
    
    public override void Cleanup()
    {
        //_t.Wait();
    }
    
    public override void Main()
    {
        // i want this to be interrupted if the polling task started in startup throws. 
        //Right here simulates running test steps, 
        //any of which can access the data that's been updated by the "background" task at any time
        try
        {
            int i = 0;
            while (i < 10)
            {
                Task.Delay(1000).Wait();
                Console.WriteLine("step" + i + "Latest: " + data.latest());
                i++;
            }
        }
        catch(Exception ex)
        {
            Console.WriteLine("EXCEPTION");
        }
    }

    public override void Setup()
    {
        _t = Task.Run(async () =>
        {
            int i =0;
            while (i < 10)
            {
                await Task.Delay(200);
                Console.WriteLine(i);
                i++;
                //i would parse incoming data and add to array here
                data.add(i);
                if (i > 3) throw new Exception("UH OH");
            }
        });
    }
}

【问题讨论】:

  • 是否要在主任务发生异常时终止子任务?类似的东西?
  • 相反,如果子任务 '_t' 抛出,我想终止 main。如果我的解析器爆炸了,那么传入的数据有问题,导致 Main() 方法的结果无效。
  • 更改代码以执行所需操作的最简单方法是让主循环在每次迭代时检查Task.IsFaulted。但是,子任务允许异常从任务中冒出可能是个坏主意。相反,您应该考虑传入 CancellationTokenSource 以允许子任务在出错时从 catch 块向主循环发出信号。
  • Main 你可以做Task.WaitAny(Task.Delay(1000), _t);
  • @PanagiotisKanavos 可能是正确的解决方案。正如上面提到的 charlieface

标签: c# exception task task-parallel-library


【解决方案1】:

我想我最终会得到类似下面的结果。不幸的是,这迫使这些测试过程的开发人员需要将所有与串行端口读取器并行执行的测试步骤与一个任务包装起来,但也许我可以将实现细节抽象到一个类中,并使用一个 API 来管理这个对他们来说更好一点。

public override void Main()
{
    Task steps = Task.Run(async () =>
    {
        int i = 1;
        //this represents step 1
        await Task.Delay(5000);
        i++;
        Console.WriteLine("Step " + i);

        //this represents step 2
        await Task.Delay(5000);
        i++;
        Console.WriteLine("Step " + i);

        //this represents step 3
        await Task.Delay(5000);
        i++;
        Console.WriteLine("Step " + i);

        //this represents step 4
        await Task.Delay(5000);
        i++;
        Console.WriteLine("Step " + i);
    });

    Task.WaitAny(_t, steps);
}

【讨论】:

    【解决方案2】:

    您可以调查调用任务代码上的Task.ContinueWith 方法。 这个ContinueWith 允许在完成和/或异常时触发委托回调。所以也许在 ContinueWith 委托中,检查任务是否成功完成,如果没有,则在此处引发一个新异常,并将抛出的异常附加给委托。

    您可以使用下面的代码作为示例:

    Task t1 = factory.StartNew(() =>
    {
        DoSomething();
    }).ContinueWith((t2) =>
    {
        if (t2.IsCanceled)
            DoSomethingWhenCancelled();
        else if (t2.IsFaulted)
            DoSomethingOnError(t1.Exception);
        else
            DoSomethingWhenComplete();
    });
    

    【讨论】:

    • 或使用awaitawait Task.Run(()=&gt;DoSomething()); 将重新抛出任何异常
    • 很好的答案,但是您可以通过使用 TaskContinuationOptionsContinueWith 重载来做得更好,它可以准确指定继续运行的时间(例如,仅在成功或错误时) .
    • 我试过了,但是如果我在这里重新抛出异常,它不会中断 main 方法的执行。
    猜你喜欢
    • 1970-01-01
    • 2015-12-28
    • 1970-01-01
    • 1970-01-01
    • 2011-07-31
    • 2017-03-08
    • 2012-03-19
    • 2013-04-21
    • 1970-01-01
    相关资源
    最近更新 更多