记一次Task抛异常,调用线程处理而引发的一些随想

多线程调用,任务线程抛出异常如何在另一个线程(调用线程)中捕获并进行处理的问题。

1.任务线程在任务线程执行语句上抛出异常。

例如:

  1. private void button2_Click(object sender, EventArgs e)
  2. {
  3. try
  4. {
  5. var task = Task.Factory.StartNew<bool>(() =>
  6. {
  7. //Do Some Things
  8. throw new Exception("Task Throw Exception!");
  9. //return true;
  10. });
  11.  
  12. //var result = task.Wait(20000);
  13. var result = task.Result;
  14. }
  15. catch (Exception ex)
  16. {
  17.  
  18. }
  19.  
  20. }

调试结果:在Task.Rrsult或者Wait时可以抛出任务异常,并在调用线程中通过try-catch捕获处理。

 2.任务线程在异步委托执行语句上抛出异常。

  1. private void button3_Click(object sender, EventArgs e)
  2. {
  3. var fun = new Func<int>(() =>
  4. {
  5. //do sonmething
  6. throw new Exception("Task Throw Exception!");
  7. return ;
  8. });
  9. try
  10. {
  11. var task = Task.Factory.StartNew<bool>(() =>
  12. {
  13. try
  14. {
  15. var res = fun.BeginInvoke(null, null);
  16. //do some thing
  17. var ob = fun.EndInvoke(res);
  18. }
  19. catch (Exception ex)
  20. {
  21.  
  22. throw ex;
  23. }
  24. return true;
  25. });
  26. var result = task.Wait();
  27. //var result1 = task.Result;
  28. }
  29. catch (Exception ex)
  30. {
  31.  
  32. }
  33. }

调试可知:异步委托在调用EndInvoke(res)获取结果时可以捕获委托内部异常并抛出由外部Task抓取。

 2.任务线程在窗口句柄(创建控件)线程上抛异常现象。

control.invoke(参数delegate)方法:在拥有此控件的基础窗口句柄的线程上执行指定的委托。

control.begininvoke(参数delegate)方法:在创建控件的基础句柄所在线程上异步执行指定委托。

即invoke表是同步、begininvoke表示异步。但是如何来进行同步和异步呢?

 2.1Invoke方法执行规则

Invoke的原理是借助消息循环通知主线程,并且在主线程执行委托。直接代码查看:

  1. private void button1_Click(object sender, EventArgs e)
  2. {
  3. //Invoke的原理是借助消息循环通知主线程,并且在主线程执行委托。
  4. try
  5. {
  6. var thIdMain = Thread.CurrentThread.ManagedThreadId;
  7. Console.WriteLine($"Load start: Main Thread ID:{thIdMain}");
  8. var task = Task.Factory.StartNew<bool>(() =>
  9. {
  10. var taskId = Thread.CurrentThread.ManagedThreadId;
  11. Console.WriteLine($"Task start: Task Thread ID:{taskId}");
  12. var res = this.Invoke(new Func<int>(() =>
  13. {
  14. var InvokeId = Thread.CurrentThread.ManagedThreadId;
  15. Console.WriteLine($"Invoke in: Begion Invoke Thread ID:{InvokeId}");
  16. //do sonmething
  17. return ;
  18. }));
  19. taskId = Thread.CurrentThread.ManagedThreadId;
  20. Console.WriteLine($"Invoke out ,Thread ID:{taskId}");
  21. return true;
  22.  
  23. });
  24.  
  25. thIdMain = Thread.CurrentThread.ManagedThreadId;
  26. Console.WriteLine($"Wait: Main Thread ID:{thIdMain}");
  27. var CanLoad = task.Wait();//.Result;
  28. thIdMain = Thread.CurrentThread.ManagedThreadId;
  29. Console.WriteLine($"End: Main Thread ID:{thIdMain}");
  30. }
  31. catch (Exception) { }
  32. }

执行输出:

Load start: Main Thread ID:1
Wait: Main Thread ID:1
Task start: Task Thread ID:3
End: Main Thread ID:1
Invoke in: Begion Invoke Thread ID:1
Invoke out ,Thread ID:3

查看输出顺序说明:invoke在主线程中执行,但是,invoke后面的代码必须在Invoke委托方法执行完成后,才能继续执行;而invoke在主线程中执行,所以其执行时机无法确定,得等消息循环(主线程)中其它消息执行后才能进行。

 2.2BeginInvoke方法执行规则

BeginInvoke也在主线程执行相应委托。直接代码查看:

  1. private void button1_Click(object sender, EventArgs e)
  2. {
  3. //BeginInvoke
  4. try
  5. {
  6. var thIdMain = Thread.CurrentThread.ManagedThreadId;
  7. Console.WriteLine($"Load start: Main Thread ID:{thIdMain}");
  8. var task = Task.Factory.StartNew<bool>(() =>
  9. {
  10. var taskId = Thread.CurrentThread.ManagedThreadId;
  11. Console.WriteLine($"Task start: Task Thread ID:{taskId}");
  12. var res = this.BeginInvoke(new Func<int>(() =>
  13. {
  14. var BegionInvokeId = Thread.CurrentThread.ManagedThreadId;
  15. Console.WriteLine($"BeginInvoke in: Begion Invoke Thread ID:{BegionInvokeId}");
  16. //do sonmething
  17. return ;
  18. }));
  19. taskId = Thread.CurrentThread.ManagedThreadId;
  20. Console.WriteLine($"BeginInvoke is Completed: {res.IsCompleted}, Thread ID:{taskId}");
  21. var ob = this.EndInvoke(res);
  22. taskId = Thread.CurrentThread.ManagedThreadId;
  23. Console.WriteLine($"BeginInvoke out ,Thread ID:{taskId}");
  24. // Console.WriteLine(ob.ToString());
  25. return true;
  26. });
  27. long i = ;
  28. while (i < )//延时
  29. {
  30. i++;
  31. }
  32. thIdMain = Thread.CurrentThread.ManagedThreadId;
  33. Console.WriteLine($"Wait: Main Thread ID:{thIdMain}");
  34. //var CanLoad = task.Wait(2000);//.Result;
  35. thIdMain = Thread.CurrentThread.ManagedThreadId;
  36. Console.WriteLine($"End: Main Thread ID:{thIdMain}");
  37. //Console.WriteLine(CanLoad);
  38. }
  39. catch (Exception) { }
  40. }

执行输出:

Load start: Main Thread ID:1
Task start: Task Thread ID:3
BeginInvoke is Completed: False, Thread ID:3
Wait: Main Thread ID:1
End: Main Thread ID:1
BeginInvoke in: Begion Invoke Thread ID:1
BeginInvoke out ,Thread ID:3

根据输出结果可知begininvoke所提交的委托方法也是在主线程中执行,BeginInvoke is Completed: False, Thread ID:3与Wait: Main Thread ID:1两段比较,会发现begininvoke提交委托方法后,子线程继续执行,不需要等待委托方法的完成。

总结:invoke和begininvoke都是在主线程中执行。invoke提交的委托方法执行完成后,才能继续执行;begininvoke提交委托方法后,子线程继续执行。invoke(同步)和begininvoke(异步)的含义,是相对于子线程而言的,实际上对于控件的调用总是由主线程来执行。

 2.3 Control.BeginInvoke或者Control.Invoke执行委托时抛出异常

Control.Invoke执行委托时抛出异常:

  1. private void button2_Click(object sender, EventArgs e)
  2. {
  3. try
  4. {
  5. var task = Task.Factory.StartNew<bool>(() =>
  6. {
  7. try
  8. {
  9. //Do Some Things
  10. var res = this.Invoke(new Func<int>(() =>
  11. {
  12. //do sonmething
  13. throw new Exception("Task Throw Exception!");
  14. return ;
  15. }));
  16. }
  17. catch (Exception ex)
  18. {
  19.  
  20. throw ex;
  21. }
  22. return true;
  23. });
  24. }
  25. catch (Exception ex)
  26. {
  27.  
  28. }
  29. }

执行结果:只有task中的try可以捕捉,继续抛出,主线程捕捉不到 

原因分析:button2_Click方法和task中invoke都是在主线程中执行。但是,invoke必须等主线程中其它消息执行完即button2_Click代码执行完退出才有机会执行。此时button2_Click方法执行完,所分配的内存空间被回收(失效),故即便task继续抛异常均不能捕获到。而Invoke在执行完成时,task后续代码阻断并等待其执行完,后续执行代码与其在内存上属于同一 堆栈,故可以捕获到Invoke抛出的异常。

Control.BeginInvoke执行委托时抛出异常:

  1. private void button2_Click(object sender, EventArgs e)
  2. {
  3. try
  4. {
  5. var task = Task.Factory.StartNew<bool>(() =>
  6. {
  7. try
  8. {
  9. //Do Some Things
  10. var res = this.BeginInvoke(new Func<int>(() =>
  11. {
  12. //do sonmething
  13. throw new Exception("Task Throw Exception!");
  14. return ;
  15. }));
  16.  
  17. var ob = this.EndInvoke(res);
  18. }
  19. catch (Exception ex)
  20. {
  21.  
  22. throw ex;
  23. }
  24. return true;
  25. });
  26. }
  27. catch (Exception ex)
  28. {
  29.  
  30. }
  31.  
  32. }

执行结果:均无法捕捉异常。但是Main函数中可以。

原因分析:button2_Click方法和task中BeginInvoke都是在主线程中执行。但是,BeginInvoke须等主线程中其它消息执行完即button2_Click代码执行完退出才有机会执行。此时button2_Click方法执行完,所分配的内存空间被回收(失效),故即便task继续跑异常均不能捕获到。而BeginInvoke在执行完成时,task后续代码无须阻断等待其执行完,二者在内存上不属于同一 堆栈, 而异步调用时,异步执行期间产生的异常由CRL库捕获,你并一般在调用EndInvoke函数获取执行结果时CRL会抛出引发异步执行期间产生的异常,但是,CRL对Control.BeginInvoke特殊处理并未抛出(个人猜想,待验证)。故此时Task无法捕获到BeginInvoke抛出的异常。

一般BeginInvoke与Invoke主要用于更新控件相关属性值,特意抛异常的可能性应该比较小,如果有异常可以在该委托里面就进行解决了。此处仅作对技术研究的一个记录。

记一次Task抛异常,调用线程处理而引发的一些随想的更多相关文章

  1. WPF异常“调用线程无法访问此对象,因为另一个线程拥有该对象 ”

    WPF中在对界面进行操作的时候,可能会遇到"调用线程无法访问此对象,因为另一个线程拥有该对象"异常,这是因为WPF中只有UI线程才能操作UI元素,非UI线程要访问UI时就会报异常了 ...

  2. 线程执行synchronized同步代码块时再次重入该锁过程中抛异常,是否会释放锁

    一个线程执行synchronized同步代码时,再次重入该锁过程中,如果抛出异常,会释放锁吗? 如果锁的计数器为1,抛出异常,会直接释放锁: 那如果锁的计数器为2,抛出异常,会直接释放锁吗? 来简单测 ...

  3. ABP在领域事件中异步调用方法抛异常

    在领域事件中调用UserRegistrationManager.RegisterAsync抛异常 Call UserRegistrationManager.RegisterAsync() throw ...

  4. CloudStack的VO在调用setRemoved方法抛异常的原因

    今天在开发中发现一个问题,本来想对一个VO对象的removed值赋值,然后去update一下这条记录,一个最简单的set方法,但是在调用时直接抛异常了. 1: public void setRemov ...

  5. C# 线程抛异常

    异常抛出 异常抛出要在线程代码中抛出,否则捕获不到 using System; using System.Threading; namespace testthread_keyword_lock { ...

  6. 使用Task简化Silverlight调用Wcf

    原文http://www.cnblogs.com/lemontea/archive/2012/12/09/2810549.html 从.Net4.0开始,.Net提供了一个Task类来封装一个异步操作 ...

  7. 01 语言基础+高级:1-7 异常与多线程_day05 【异常、线程】

    day05 [异常.线程] 主要内容 异常.线程 教学目标 能够辨别程序中异常和错误的区别 说出异常的分类 说出虚拟机处理异常的方式 列举出常见的三个运行期异常 能够使用try...catch关键字处 ...

  8. 288 day05_异常,线程

    day05 [异常.线程] 主要内容 异常.线程 教学目标 [ ] 能够辨别程序中异常和错误的区别 [ ] 说出异常的分类 [ ] 说出虚拟机处理异常的方式 [ ] 列举出常见的三个运行期异常 [ ] ...

  9. poco json 中文字符,抛异常JSON Exception -->iconv 转换 备忘录。

    起因 最近linux服务器通信需要用到json. jsoncpp比较出名,但poco 1.5版本以后已经带有json库,所以决定使用poco::json(linux 上已经用到了poco这一套框架). ...

随机推荐

  1. 数据结构-ST表

    数据结构-ST表 不可修改,在线查询的 RMQ 问题. 其中 \(f[i][j]\) 表示 \(i\sim i+(1<<j)-1\) 这段的 RMQ 值. 时间复杂度 \(O(n\log ...

  2. Python知识点 - Xpath提取某个标签,需要转换为HTML。

        # lxml转Html from lxml import etree from HTMLParser import HTMLParser def lxml_to_html(text:etree ...

  3. springboot1.5.9整合websocket实现实时显示的小demo

    最近由于项目需要实时显示数据库更新的数据变化情况,一开始想过在前端使用ajax异步轮询方法实现,但后面考虑到性能和流量等要求,就放弃该方法而选择使用websocket(毕竟现在springboot整合 ...

  4. 数据库--Redis

    原因: 源码是官方configure过的,但官方configure时,生成的文件有时间戳信息,所以如果你的虚拟机的时间不对,比如说是2022年,就可能会出错 解决: date -s ‘yyyy-mm- ...

  5. java 几种锁实现

    public class SyncronizedTest { private int value = 1; private AtomicInteger value1 = new AtomicInteg ...

  6. 2020 webstorm 最新激活方式 有效期2021年11月 可用

    MIIElT25XE-eyJsaWNlbnNlSWQiOiJPUVQzT0oyNVhFIiwibGljZW5zZWVOYW1lIjoi5rC45LmF5r+A5rS7IGlkZWEubWVkZW1pb ...

  7. java.lang.reflect.UndeclaredThrowableException: null Caused by: org.apache.zookeeper.KeeperException$UnimplementedException: KeeperErrorCode = Unimplemented for

    java.lang.reflect.UndeclaredThrowableException: null    at org.springframework.util.ReflectionUtils. ...

  8. 大数据学习之scala-环境搭建

    scala 下载网站 https://www.scala-lang.org/download/ 安装scala要先安装java,并且配置java环境,官网也有说明 不过国内的网站下载不下来可以访问:  ...

  9. des 加密解密工具类

    最近在做des的双对称加密解密,特此记录一下. des对称加密,是一种比较传统的加密方式,其加密运算.解密运算使用的是同样的密钥,信息的发送者和信息的接收者在进行信息的传输与处理时,必须共同持有该密码 ...

  10. c strncpy 容易出错的地方

    使用strncpy的是注意两点,目的是数组和目的是指针 .目的是数组: ] = "abcde"; // ] = "; strncpy(dest,src,N); dest[ ...