C#多线程中的异常处理操作示例
本文实例讲述了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 输出
补充:
假如在前驱任务中出现了异常,如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 输出
其实这样也可以理解,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 输出
正常取消的任务最终状态是 rantocompletion ,这里要注意的是,cancelafter()是在这个方法调用的那一刻开始计时的(并非以run开始计时,好吧,很好理解,我却疑惑了半天)
小结:
结合 taskcontinuationoptions 和 cancellationtokensource 可以很好处理多任务中异常,但是编写在异步程序还是很繁琐的,具体的在下一个笔记中会结合c#5.0做一个比较
更多关于c#相关内容感兴趣的读者可查看本站专题:《c#常见控件用法教程》、《winform控件用法总结》、《c#数据结构与算法教程》、《c#面向对象程序设计入门教程》及《c#程序设计之线程使用技巧总结》
希望本文所述对大家c#程序设计有所帮助。