欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#多线程中的异常处理操作示例

程序员文章站 2022-06-03 18:26:14
本文实例讲述了c#多线程中的异常处理操作。分享给大家供大家参考,具体如下: 常规thread中处理异常 使用thread创建的子线程,需要在委托中捕捉,无法在上下文线程...

本文实例讲述了c#多线程中的异常处理操作。分享给大家供大家参考,具体如下:

常规thread中处理异常

使用thread创建的子线程,需要在委托中捕捉,无法在上下文线程中捕捉

static void main(string[] args)
{
  threadstart threadstart = dowork;
  thread thread = new thread(threadstart);
  thread.start();
  thread.join();
}
static void dowork()
{
  try
  {
    throw new exception("子线程出现异常了");
  }
  catch (exception ex)
  {
    trace.assert(false, "catch in delegate");
  }
}

task中处理异常

1.仍然可以在委托中捕获异常

2.可以捕获task.wait() 或者 task.result 的 aggregateexception 异常

try
{
  task.wait();
}
catch (aggregateexception ex)
{
  console.writeline($"error: {ex.gettype().name}");
  foreach (exception item in ex.innerexceptions)
  {
    console.writeline($"{item.gettype().name}, {item.message}");
  }
}

aggregateexception 是并行任务中捕获的一组异常

通过延续任务捕获前驱任务中的异常

static void main(string[] args)
{
  task task = task.run(() => throw new exception("前驱任务异常了"));
  task faultedtask = task.continuewith(antecedenttask =>
  {
    antecedenttask.exception.handle(eache =>
    {
      console.writeline($"error: {eache.message}");
      return true;
    });
  },taskcontinuationoptions.onlyonfaulted);
  faultedtask.wait();
}

前驱任务:使用run书写的第一个任务就是前驱任务

延续任务:在一个任务后使用continuewith添加的任务就是延续任务,延续一般是一个全新的工作线程

taskcontinuationoptions:指定延续任务时的可配置项,默认情况下前驱任务完成后,立即执行延续任务,onlyonfaulted表示只有前驱任务失败(出异常的时候)才会执行这一个延续任务

task.exception也是一个aggregateexception 异常

注意:

1.当指定的taskcontinuationoptions与前驱任务运行结果不一致时,强制调用延续任务wait()会引发taskcanceledexception异常

static void main(string[] args)
{
  task task = new task(() =>
  {
    console.writeline("前驱动任务执行中...");
  });
  task faultedtask = task.continuewith(antecedenttask =>
  {
    console.writeline("延续动任务执行中...");
  }, taskcontinuationoptions.onlyonfaulted);
  task.start();
  try
  {
    faultedtask.wait();
  }
  catch (aggregateexception ex)
  {
    console.writeline($"error: {ex.gettype().name}");
    foreach (exception item in ex.innerexceptions)
    {
      console.writeline($"{item.gettype().name}, {item.message}");
    }
  }
  console.writeline($"前驱任务状态{task.status}");
  console.writeline($"延续任务状态{faultedtask.status}");
}

ctrl+f5 输出

C#多线程中的异常处理操作示例

补充:

假如在前驱任务中出现了异常,如onlyonfaulted所愿,会执行faultedtask任务,并且在faultedtask.wait()中不会捕捉到前驱任务的异常,具体看下面一点

2.延续任务虽然在异步任务中提供了类似if else 的continuewith但是在异常处理上还是有点局限,看一个例子

static void main(string[] args)
{
  task task = task.run(()
    =>
  throw new exception("前驱任务异常了"));
  task task1 = task.continuewith(antecedenttask =>
  {
    throw new exception("延续任务1异常了");
  });
  task task2 = task1.continuewith(antecedenttask =>
  {
    throw new exception("延续任务2异常了");
  });
  task task3 = task2.continuewith(antecedenttask =>
  {
    throw new exception("延续任务3异常了");
  });
  try
  {
    task3.wait();
  }
  catch (aggregateexception ex)
  {
    console.writeline($"error: {ex.gettype().name}");
    foreach (exception item in ex.innerexceptions)
    {
      console.writeline($"{item.gettype().name}, {item.message}");
    }
  }
}

ctrl+f5 输出

C#多线程中的异常处理操作示例

其实这样也可以理解,task3.wait()只会收集task3所在工作线程上的异常,遗憾的是task.exception.innerexceptions是一个只读集合,这样一来,每个任务的异常只能在各自委托中处理了,事实上也应该如此,可以使用taskcontinuationoptions进行灵活控制

使用cancellationtokensource取消任务

static void main(string[] args)
{
  cancellationtokensource cancellationtokensource = new cancellationtokensource();
  cancellationtokensource.token.register(() => 
  {
    console.writeline("任务取消了");
  });
  cancellationtokensource.cancelafter(2000);
  task task = task.run(() =>
  {
    while (true && !cancellationtokensource.iscancellationrequested)
    {
      console.writeline("任务执行中...");
      thread.sleep(300); 
    }
  },
  cancellationtokensource.token);
  task.wait();
  console.writeline($"任务的最终状态是:{task.status}");
}

ctrl+f5 输出

C#多线程中的异常处理操作示例

正常取消的任务最终状态是 rantocompletion ,这里要注意的是,cancelafter()是在这个方法调用的那一刻开始计时的(并非以run开始计时,好吧,很好理解,我却疑惑了半天)

小结:

结合 taskcontinuationoptions 和 cancellationtokensource 可以很好处理多任务中异常,但是编写在异步程序还是很繁琐的,具体的在下一个笔记中会结合c#5.0做一个比较

更多关于c#相关内容感兴趣的读者可查看本站专题:《c#常见控件用法教程》、《winform控件用法总结》、《c#数据结构与算法教程》、《c#面向对象程序设计入门教程》及《c#程序设计之线程使用技巧总结

希望本文所述对大家c#程序设计有所帮助。