在取消Task.Run时未捕获到异常

时间:2014-12-26 16:57:49

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

我有一个班级Worker正在做一些工作(模拟工作量):

public class Worker
    { ...

public void DoWork(CancellationToken ct)
        {
            for (int i = 0; i < 10; i++)
            {
                ct.ThrowIfCancellationRequested();
                Thread.Sleep(2000);
            }
        }

现在我想在Task.Run(来自我的Windows窗体应用程序,按下按钮)中使用此方法,可以取消:

private CancellationTokenSource _ctSource;

try
            {
                Task.Run(() =>
                {
                    _worker.DoWork(_ctSource.Token);
                },_ctSource.Token);
            }
            catch (AggregateException aex)
            {
                String g = aex.Message;
            }
            catch (OperationCanceledException ex)
            {
                String g = ex.Message;
            }
            catch (Exception ex)
            {
                String g = ex.Message;
            }

但是当任务开始时,我无法用_ctSource.Cancel()取消它; 我在visual studio中收到错误,OperationCanceledException未处理!

但我在try-catch子句中包围了Task.Run调用! Worker对象中出现的异常应该抛出还是不抛出? 有什么问题?

3 个答案:

答案 0 :(得分:2)

您的Task.Run来电会创建任务,然后立即返回。它永远不会抛出。但它创建的任务可能会失败或被取消稍后

这里有几个解决方案:

  • 使用await

    await Task.Run(...)
    
  • 根据失败/取消情况附加续篇:

    var task = Task.Run(...);
    task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnCanceled);
    task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnFaulted);
    
  • 在失败时附加一个延续:

    Task.Run(...).ContinueWith(t => ..., TaskContinuationOptions.NotOnRanToCompletion);
    

您可以/应该使用的解决方案取决于周围的代码。

答案 1 :(得分:0)

您需要更新令牌
private CancellationTokenSource _ctSource = new CancellationTokenSource();

为什么要在DoWork中投入期望?
一个线程的异常不会冒泡另一个启动该线程的线程。

Cancellation in Managed Threads

答案 2 :(得分:0)

如果并行任务抛出异常,它将返回执行并将拥有它的Exception属性(作为AggregateException,您应该检查其InnerException )set(及其IsCanceledIsFaulted属性设置为true)。我的项目中的一些最小示例代码将异常升级为主线程:

  var t = new Task(Initialize);
  t.Start();
  while (!t.IsCompleted && !t.IsFaulted)
  {
    // Do other work in the main thread
  }
  if (t.IsFaulted)
  {
    if (t.Exception != null)
    {
      if(t.Exception.InnerException != null)
        throw t.Exception.InnerException;
    }
    throw new InvalidAsynchronousStateException("Initialization failed for an unknown reason");
  }

如果您使用CancellationTokenSource,则应该很容易对其进行增强,以检查IsCanceled(而不是IsFaulted

你也可以在我的项目中使用Task.Wait()而不是while循环......在这种精确的情况下,使用while循环似乎更合适,但你需要等待{ {1}}以这种或那种方式结束。

如果您使用Task,则可以使用Task.Run()传入原始任务(您可以在其中检查.ContinueWith(Task)IsFaulted),或拥有它根据您的遗嘱,仅在故障执行时运行。