记一次Task抛异常,调用线程处理而引发的一些随想
记一次Task抛异常,调用线程处理而引发的一些随想
多线程调用,任务线程抛出异常如何在另一个线程(调用线程)中捕获并进行处理的问题。
1.任务线程在任务线程执行语句上抛出异常。
例如:
private void button2_Click(object sender, EventArgs e)
{
try
{
var task = Task.Factory.StartNew<bool>(() =>
{
//Do Some Things
throw new Exception("Task Throw Exception!");
//return true;
}); //var result = task.Wait(20000);
var result = task.Result;
}
catch (Exception ex)
{ } }
调试结果:在Task.Rrsult或者Wait时可以抛出任务异常,并在调用线程中通过try-catch捕获处理。

2.任务线程在异步委托执行语句上抛出异常。
private void button3_Click(object sender, EventArgs e)
{
var fun = new Func<int>(() =>
{
//do sonmething
throw new Exception("Task Throw Exception!");
return ;
});
try
{
var task = Task.Factory.StartNew<bool>(() =>
{
try
{
var res = fun.BeginInvoke(null, null);
//do some thing
var ob = fun.EndInvoke(res);
}
catch (Exception ex)
{ throw ex;
}
return true;
});
var result = task.Wait();
//var result1 = task.Result;
}
catch (Exception ex)
{ }
}
调试可知:异步委托在调用EndInvoke(res)获取结果时可以捕获委托内部异常并抛出由外部Task抓取。
2.任务线程在窗口句柄(创建控件)线程上抛异常现象。
control.invoke(参数delegate)方法:在拥有此控件的基础窗口句柄的线程上执行指定的委托。
control.begininvoke(参数delegate)方法:在创建控件的基础句柄所在线程上异步执行指定委托。
即invoke表是同步、begininvoke表示异步。但是如何来进行同步和异步呢?
2.1Invoke方法执行规则
Invoke的原理是借助消息循环通知主线程,并且在主线程执行委托。直接代码查看:
private void button1_Click(object sender, EventArgs e)
{
//Invoke的原理是借助消息循环通知主线程,并且在主线程执行委托。
try
{
var thIdMain = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Load start: Main Thread ID:{thIdMain}");
var task = Task.Factory.StartNew<bool>(() =>
{
var taskId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Task start: Task Thread ID:{taskId}");
var res = this.Invoke(new Func<int>(() =>
{
var InvokeId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Invoke in: Begion Invoke Thread ID:{InvokeId}");
//do sonmething
return ;
}));
taskId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Invoke out ,Thread ID:{taskId}");
return true; }); thIdMain = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Wait: Main Thread ID:{thIdMain}");
var CanLoad = task.Wait();//.Result;
thIdMain = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"End: Main Thread ID:{thIdMain}");
}
catch (Exception) { }
}
执行输出:
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也在主线程执行相应委托。直接代码查看:
private void button1_Click(object sender, EventArgs e)
{
//BeginInvoke
try
{
var thIdMain = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Load start: Main Thread ID:{thIdMain}");
var task = Task.Factory.StartNew<bool>(() =>
{
var taskId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Task start: Task Thread ID:{taskId}");
var res = this.BeginInvoke(new Func<int>(() =>
{
var BegionInvokeId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"BeginInvoke in: Begion Invoke Thread ID:{BegionInvokeId}");
//do sonmething
return ;
}));
taskId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"BeginInvoke is Completed: {res.IsCompleted}, Thread ID:{taskId}");
var ob = this.EndInvoke(res);
taskId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"BeginInvoke out ,Thread ID:{taskId}");
// Console.WriteLine(ob.ToString());
return true;
});
long i = ;
while (i < )//延时
{
i++;
}
thIdMain = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Wait: Main Thread ID:{thIdMain}");
//var CanLoad = task.Wait(2000);//.Result;
thIdMain = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"End: Main Thread ID:{thIdMain}");
//Console.WriteLine(CanLoad);
}
catch (Exception) { }
}
执行输出:
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执行委托时抛出异常:
private void button2_Click(object sender, EventArgs e)
{
try
{
var task = Task.Factory.StartNew<bool>(() =>
{
try
{
//Do Some Things
var res = this.Invoke(new Func<int>(() =>
{
//do sonmething
throw new Exception("Task Throw Exception!");
return ;
}));
}
catch (Exception ex)
{ throw ex;
}
return true;
});
}
catch (Exception ex)
{ }
}
执行结果:只有task中的try可以捕捉,继续抛出,主线程捕捉不到

原因分析:button2_Click方法和task中invoke都是在主线程中执行。但是,invoke必须等主线程中其它消息执行完即button2_Click代码执行完退出才有机会执行。此时button2_Click方法执行完,所分配的内存空间被回收(失效),故即便task继续抛异常均不能捕获到。而Invoke在执行完成时,task后续代码阻断并等待其执行完,后续执行代码与其在内存上属于同一 堆栈,故可以捕获到Invoke抛出的异常。
Control.BeginInvoke执行委托时抛出异常:
private void button2_Click(object sender, EventArgs e)
{
try
{
var task = Task.Factory.StartNew<bool>(() =>
{
try
{
//Do Some Things
var res = this.BeginInvoke(new Func<int>(() =>
{
//do sonmething
throw new Exception("Task Throw Exception!");
return ;
})); var ob = this.EndInvoke(res);
}
catch (Exception ex)
{ throw ex;
}
return true;
});
}
catch (Exception ex)
{ } }
执行结果:均无法捕捉异常。但是Main函数中可以。

原因分析:button2_Click方法和task中BeginInvoke都是在主线程中执行。但是,BeginInvoke须等主线程中其它消息执行完即button2_Click代码执行完退出才有机会执行。此时button2_Click方法执行完,所分配的内存空间被回收(失效),故即便task继续跑异常均不能捕获到。而BeginInvoke在执行完成时,task后续代码无须阻断等待其执行完,二者在内存上不属于同一 堆栈, 而异步调用时,异步执行期间产生的异常由CRL库捕获,你并一般在调用EndInvoke函数获取执行结果时CRL会抛出引发异步执行期间产生的异常,但是,CRL对Control.BeginInvoke特殊处理并未抛出(个人猜想,待验证)。故此时Task无法捕获到BeginInvoke抛出的异常。
一般BeginInvoke与Invoke主要用于更新控件相关属性值,特意抛异常的可能性应该比较小,如果有异常可以在该委托里面就进行解决了。此处仅作对技术研究的一个记录。
记一次Task抛异常,调用线程处理而引发的一些随想的更多相关文章
- WPF异常“调用线程无法访问此对象,因为另一个线程拥有该对象 ”
WPF中在对界面进行操作的时候,可能会遇到"调用线程无法访问此对象,因为另一个线程拥有该对象"异常,这是因为WPF中只有UI线程才能操作UI元素,非UI线程要访问UI时就会报异常了 ...
- 线程执行synchronized同步代码块时再次重入该锁过程中抛异常,是否会释放锁
一个线程执行synchronized同步代码时,再次重入该锁过程中,如果抛出异常,会释放锁吗? 如果锁的计数器为1,抛出异常,会直接释放锁: 那如果锁的计数器为2,抛出异常,会直接释放锁吗? 来简单测 ...
- ABP在领域事件中异步调用方法抛异常
在领域事件中调用UserRegistrationManager.RegisterAsync抛异常 Call UserRegistrationManager.RegisterAsync() throw ...
- CloudStack的VO在调用setRemoved方法抛异常的原因
今天在开发中发现一个问题,本来想对一个VO对象的removed值赋值,然后去update一下这条记录,一个最简单的set方法,但是在调用时直接抛异常了. 1: public void setRemov ...
- C# 线程抛异常
异常抛出 异常抛出要在线程代码中抛出,否则捕获不到 using System; using System.Threading; namespace testthread_keyword_lock { ...
- 使用Task简化Silverlight调用Wcf
原文http://www.cnblogs.com/lemontea/archive/2012/12/09/2810549.html 从.Net4.0开始,.Net提供了一个Task类来封装一个异步操作 ...
- 01 语言基础+高级:1-7 异常与多线程_day05 【异常、线程】
day05 [异常.线程] 主要内容 异常.线程 教学目标 能够辨别程序中异常和错误的区别 说出异常的分类 说出虚拟机处理异常的方式 列举出常见的三个运行期异常 能够使用try...catch关键字处 ...
- 288 day05_异常,线程
day05 [异常.线程] 主要内容 异常.线程 教学目标 [ ] 能够辨别程序中异常和错误的区别 [ ] 说出异常的分类 [ ] 说出虚拟机处理异常的方式 [ ] 列举出常见的三个运行期异常 [ ] ...
- poco json 中文字符,抛异常JSON Exception -->iconv 转换 备忘录。
起因 最近linux服务器通信需要用到json. jsoncpp比较出名,但poco 1.5版本以后已经带有json库,所以决定使用poco::json(linux 上已经用到了poco这一套框架). ...
随机推荐
- 汇编语言-[bx]和loop指令和多个段
5.1 [BX]和内存单元的描述 要完成描述一个内存单元,需要两种信息: 内存单元的地址: 可以用 [0] 表示一个内存单元, 0 表示单元的偏移地址,段地址默认在 ds 中: 同样也可以用 [bx] ...
- 微信SEO怎么做-最新微信SEO干货
星辉信息科技进行微信SEO已经很多年了,结合多年的微信SEO经验通过浅谈微信SEO.微信SEO的3大优势.微信SEO的6个排名技巧.企业和个人微信SEO的4大优化战略来讲,可以完美解决B端C端微信获客 ...
- racket学习-call/cc (let/cc)
Drracket continuation 文中使用let/cc代替call/cc Racket文档中,let/cc说明为: (let/cc k body ...+) Equivalent to (c ...
- 一个和与后台数据连接的模板get post put 以及延伸的query
/* example: require.config({ paths: { "httpClient": "../../core/http-client" } } ...
- go极其ide的安装
一.下载软件开发包 官网:https://golang.google.cn/ 二. 安装和配置SDK windows使用.msi一键安装 配置环境变量 GOROOT,自动的,默认将go安装到C:/ ...
- Hadoop集群搭建(七)~完全分布运行模式
我使用的是完全分布运行模式.上一篇安装了JDK,本篇记录Hadoop的安装,版本2.7.2 (一)配置文件 1,先将hadoop安装包解压到module目录下 2,配置hadoop-env.sh.vi ...
- 浅谈 HTTP中Get与Post的区别
浅谈 HTTP中Get与Post的区别 存在的误区 有人说 HTTP 协议下的 Get 请求参数长度是有大小限制的,最大不能超过XX,而 Post 是无限制的,看到这里,我想他们定是看多了一些以讹传讹 ...
- python的元类编程
廖雪峰的python教程有python元类编程示例,综合代码如下 https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df ...
- Redux的createStore实现
Redux的createStore实现 使用过react的同学应该对Redux这个东西有所了解.他是一种全局状态管理的思想(对, 这里我觉得它是一种思想, 因为对于React来说, 其实Redux ...
- RestFul API 统一格式返回 + 全局异常处理
一.背景 在分布式.微服务盛行的今天,绝大部分项目都采用的微服务框架,前后端分离方式.前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端. ...