新建一个winform的应用程序,添加一个计时器System.Timers.Timer类。
计时器间隔为1秒,计时器中的代码,定时刷新界面上的label控件。
在调试状态下,程序会提示异常。
但是编译好exe后,直接运行。就不会提示错误
前提:程序里面没有做异常处理。
期待的结果:程序会因为异常而崩溃。
问题:为什么程序不会崩溃?
using Timer = System.Windows.Timer; private Timer timer; private void Form1_Load(object sender, EventArgs e) { InitTimer(); } private void InitTimer() { timer = new Timer(); timer.Interval = 1000; timer.Tick += timer_Tick; timer.Enabled = true; } private const string text = "hello world"; void timer_Tick(object sender, EventArgs e) { if (label1.Text.Equals(text)) { label1.Text = "i love you"; } else { label1.Text = text; } }
winform 有主线程的即UI线程, 只要主线程不奔溃,程序就不会奔溃,下面的代码,调试会报错,但运行exe就不会
private void Form1_Load(object sender, EventArgs e) { Task.Factory.StartNew(Error); } private void Error() { throw new Exception(""); }
那UI线程什么时候会崩溃呢?
靠什么来决定的?
private void Form1_Load(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(Error); } private void Error(object obj) { throw new Exception(""); }
代码改成这样就会崩溃
我想知道,程序崩溃或者不崩溃是什么决定的?
你说是因为ui线程崩溃,那么ui是什么时候会崩溃,什么时候不会崩溃?
@ChuckLu: 不好意思,我上面的回答是错误的,首先我的代码没有奔溃是因为代码中Task的异常没有抛出来,https://www.mgenware.com/blog/?p=231,这帖子有介绍。
我运行了你贴出来的代码(最上面的),我电脑调试下是不会报错的,4.0环境,Timer控件直接操作ui(不另开线程)是不需要同步的(4.0下),我不清楚你的调试报错是怎么出来的。
程序奔溃肯定是由未处理异常引起的,你说的出了未处理异常但程序不奔溃是不可能的,
@2727551894: 额,需要注意下,我的代码示例有using Timer = System.Windows.Timer;
我的timer不在ui线程执行的,timer不是控件。不是windows.form命名空间下的。
是System.Timers.Timer下的
你修改下timer的类型,然后从测试下。会有跨线程的异常出现的。
@ChuckLu: System.Windows.Timer 你这个命名空间是怎么引用的,我本地都找不到
而且 timer 不是控件的话,你的tick事件是怎么来的?
@2727551894:
Namespace: System.Timers
Assembly: System (in System.dll)
https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx
可能是我代码复制错了,之前我也用了timer控件的。
Elapsed正确的事件,应该是这个
@ChuckLu:
https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.100).aspx
你看Example上面的说明,这个事件里的异常会自动处理掉(大概意思),调试的时候,有调试器在,所以会抛出异常来。推荐你不要使用这个类,看说明很坑
@ChuckLu: 补充一下,一般情况下只要有未处理的异常,程序就会奔溃的,除了这种特殊的Class
@2727551894: 非常感谢,我忘记去看Remark了。
The Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event. This behavior is subject to change in future releases of the .NET Framework.
你想要的崩溃的结果是什么?跳到源代码?还是弹出对话框提示异常?还是直接关机重起?
之前程序有未处理的异常,然后程序就崩溃了。
和源代码无关的,直接运行的exe。
异常未处理,不需要弹出对话框。
和关机有什么关系?不懂。
正常的逻辑,应该是程序崩溃掉
@ChuckLu:我的意思是 你想要程序崩溃,你这个崩溃指得是什么?
是 这种 ?
还是这种:
@田麦成: 对的,类似这种的崩溃。
@ChuckLu: 贴上你的代码……
winform里面的异常看起来是这样的,程序直接崩溃掉了
private void Form1_Load(object sender, EventArgs e) { Method(); } private void Method() { int a = 1; int b = 0; int c = a / b; }
@ChuckLu:
@田麦成:
还有这种错误
private void Form1_Load(object sender, EventArgs e) { Thread thread = new Thread(Method); thread.IsBackground = true; thread.Start(); } private void Method() { int a = 1; int b = 0; int c = a/b; }
@ChuckLu:能看一下代码么?
@田麦成: 代码已经上传了
@田麦成:
private void Form1_Load(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(Method); } private void Method(object obj) { int a = 1; int b = 0; int c = a / b; }
这个也会导致崩溃。
@ChuckLu:看不出来哪里错了
@田麦成: System.Windows.Timer计时器是运行在其他线程上的,不是ui线程。
在这个上面对控件进行更新的话,调试模式下,会出现跨线程调用控件的异常提示。
但是编译成exe,打开运行之后,就会出问题
@ChuckLu:你给得只是只字片言的代码,我看不出来问题的。断章取义没办法的
@田麦成: 只言片语?已经是完整的代码了呀。
winform上面,拖了一个label控件,控件名是label1.
代码已经非常的详尽了
forms.timer是运行在ui线程,timers.timer是多线程的,对于跨线程操作需要invoke。
看了上面的回复,你的意思是为什么线程里面的错误没有造成程序崩溃?
因为转web很久,对此不太了解,我的想法是,线程中的错误没有影响到UI线程,程序得以继续运行。
而调试的时候,IDE会帮助捕获一切错误。
之前别人给我讲解的时候,我也考虑到这个了。但是我后面新开一个线程或者在线程池上执行这一段代码。仍然会导致程序崩溃。上面倒数2个代码示例就演示这种情况。
在非ui线程触发了这个异常,但是程序崩溃了
@ChuckLu: 我想了一下,记得好像以前看了一篇文章,在2.0的时候是可以跨线程操作的。 我的想法:跨线程操作是可以的,但是如果2个线程同时操作一个控件可能会造成问题。于是IDE在调试的时候就检查这些不安全代码,并报错(此时不一定会真的出错了)。 ctrl+f5 直接运行的时候,这些不安全代码如果没有出问题,也就不报错了... 就像添加了CheckForIllegalCrossThreadCalls = false; 一样,对于跨线程访问控件不出问题就不管了。
@上位者的怜悯: 不明白。我没有加这个CheckForIllegalCrossThreadCalls = false; 就应该出错呀。
如果我在计时器里面添加try catch是会捕获到这个异常的。
说明异常肯定是发生了,但是没有导致程序崩溃。
@ChuckLu: 程序并没有出错,所以没有崩溃。【checkfor...=false】是让编辑器对这种跨线程访问的不安全代码不报错。 实际运行的时候,这种不安全代码如果没有出现问题也不会报错,如果真的有多个线程同时操作一个控件,就会崩溃了。 跨线程直接访问只是不安全,不一定会出错。
___________ 个人见解。
@上位者的怜悯: 不是呀。你看其他的代码示例。程序崩溃,就是因为跨线程调用控件导致的
@ChuckLu: 调试的时候报错,是IDE帮你检测到了不安全代码,而实际上这代码运行起来不一定出错。
除以0 当然100%报错啊者不考虑了。
@上位者的怜悯: 除以0,程序貌似不会崩溃
@ChuckLu: 我解释不来,上面都是思考之后的想法,对于这个情况我只能这么解释。
除以0我没有测试,只试了ctrl+f5跨线程访问的。
不影响UI线程,或者没有真的出错,就不报错了。 不过这种说法我自己都信服不来,帮不了你。
主线程不崩溃 winform就不会崩溃
子线程影响不到主线程
至于子线程报错 调试程序是可以捕获到的 这个没有冲突
private void Form1_Load(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(Method); } private void Method(object obj) { int a = 1; int b = 0; int c = a / b; }
private void Form1_Load(object sender, EventArgs e) { Thread thread = new Thread(Method); thread.IsBackground = true; thread.Start(); } private void Method() { int a = 1; int b = 0; int c = a/b; }
上面2段代码也是子线程吧?这个就会导致程序崩溃。
winfrom不太熟, 路过。