【问题标题】:c# how to propagate a Task exception to the main thread [closed]c#如何将Task异常传播到主线程[关闭]
【发布时间】:2017-08-30 13:04:25
【问题描述】:

我有下面的代码 - 我有一个在控制台应用程序的后台永久运行的线程。如果在该任务中引发异常,我需要传播到主线程。我该怎么做?

    private void StartTask()
    {
        Task.Run(() =>
        {

                while (_subStatus)
                {
                 // do work here
                 // an exception might be thrown
                }

        });
    }

编辑: 我不够清楚。该任务旨在在应用程序的整个生命周期内运行。它不需要返回结果。我确实想知道任务中抛出了一个异常。它看起来像这样:

    static void Main(string[] args)
    {
       //setup
       StartTask()
       // continue to do other work in the main thread
       while (true)
       {
         // main thread work
       }
    }

EDIT2:使用以下代码,我至少可以记录错误

    private async Task StartTask()
    {
       try{
        await Task.Run(() =>
        {

                while (_subStatus)
                {
                 // do work here
                 // an exception might be thrown
                }

        });
        }catch(exception e)
        { //log exception here, still not on the main thread though }
    }

最终编辑: 从底部的这个答案:https://github.com/Topshelf/Topshelf/issues/245 对于长时间运行的任务(线程),我应该执行以下操作:

new Thread(() => {
// your exception here will stop the service or take down the app
}).Start();

不是这个:

Task.Run(() => {
// your thrown exception here won't stop the service
});

【问题讨论】:

  • 好吧,如果你 await 任务(无论如何你都应该这样做),那么你可以将它包装在 try-catch
  • 如果这需要永久运行,这是否意味着循环需要在异常后继续?如果是这样,它可以立即继续,还是需要等待主线程完成处理异常?
  • “永久运行”让我觉得你的主线程中有某种循环或等待点 - 否则控制台程序将退出。您能添加一个简单的代码示例吗?
  • 你的问题太宽泛了。正如@MickyD 所指出的,通过观察任务(例如await 或尝试检索Result 或检查State,将设置为FaultedException 属性),您可以看到主线程中的异常。但是你的主线程在做什么?如果没有一个好的minimal reproducible example 可以清楚地说明您的场景,就无法说出哪种方法适用于您的场景。
  • 我的第二次编辑显示了我想要实现的目标,我迫不及待地等待任务完成,因为如果没有例外,它永远不会完成。我的主线程处理来自后台线程的事件。我希望能够在主线程上看到后台任务异常。否则,该任务会静默失败。

标签: c# multithreading exception task


【解决方案1】:

这使得worker和main这两个线程都可以被监控,如果一个抛出异常,应用程序将退出,或者你可以抛出异常

工作线程有一个监控线程,它可以处理异常并在不退出的情况下做一些有意义的事情,或者在这种情况下它会重新抛出并将其发送回将退出应用程序的主线程。

static void Main(string[] args)
{
    var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;

    var worker = Task.Run(async () =>
    {
        try
        {
            var task = Task.Run(() => throw new Exception("hi"));
            await task;
        }
        catch (Exception e)
        {
            Console.WriteLine("Async...");
            throw;
        }

    }, token);

    var main = Task.Run(() =>
    {
        while (!token.IsCancellationRequested)
        {
            Thread.Sleep(1);
        }
    }, token);

    var tasks = new[] { worker, main };
    var index = Task.WaitAny(tasks);

    var taskThatStopped = tasks[index];
    if (taskThatStopped.IsFaulted)
    {
        Console.WriteLine("Task quit because of a fault");
        Console.WriteLine(taskThatStopped.Exception.InnerException.Message);
    }
    tokenSource.Cancel();
    try
    {
        Task.WaitAll(tasks);
    }
    catch (Exception e)
    {
    }
}

【讨论】:

  • 对不起,也不得不对此投反对票。 OP 线程在后台运行,而您的线程由于 Wait() 而阻塞了主线程 - 所以它并没有真正在后台运行
  • 约翰是正确的(虽然有点苛刻)。此解决方案将阻止主线程继续运行。
  • @John 我展示了两种变体来在预期点传播异常,我永远不会尝试在主线程上导致不受控制的异常。但是你可以做一些事情,比如我这里有一个线程监视另一个线程。
  • @KeithNicholas 如果后台线程出现意外异常,我确实希望主线程失败。否则它会静默失败,应用程序会继续运行。如果有错误,我希望它尽快失败。
  • 为什么不优雅地处理呢?
猜你喜欢
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
  • 2014-03-20
  • 2016-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-17
相关资源
最近更新 更多