首页 新闻 赞助 找找看

winform中出现“跨线程调用异常”,程序为什么不崩溃?

0
[已解决问题] 解决于 2015-11-13 11:32

新建一个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;
            }
        }
ChuckLu的主页 ChuckLu | 小虾三级 | 园豆:514
提问于:2015-11-10 10:00
< >
分享
最佳答案
0

 winform 有主线程的即UI线程, 只要主线程不奔溃,程序就不会奔溃,下面的代码,调试会报错,但运行exe就不会

  private void Form1_Load(object sender, EventArgs e)
        {
            Task.Factory.StartNew(Error);
        }

        private void Error()
        {
            throw new Exception("");
        }
奖励园豆:5
xmj112288 | 初学一级 |园豆:126 | 2015-11-12 09:53

那UI线程什么时候会崩溃呢?

靠什么来决定的?

  private void Form1_Load(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(Error);
        }

        private void Error(object obj)
        {
            throw new Exception("");
        }

代码改成这样就会崩溃

 

我想知道,程序崩溃或者不崩溃是什么决定的?

你说是因为ui线程崩溃,那么ui是什么时候会崩溃,什么时候不会崩溃?

 

ChuckLu | 园豆:514 (小虾三级) | 2015-11-12 10:02

@ChuckLu: 不好意思,我上面的回答是错误的,首先我的代码没有奔溃是因为代码中Task的异常没有抛出来,https://www.mgenware.com/blog/?p=231,这帖子有介绍。

我运行了你贴出来的代码(最上面的),我电脑调试下是不会报错的,4.0环境,Timer控件直接操作ui(不另开线程)是不需要同步的(4.0下),我不清楚你的调试报错是怎么出来的。

程序奔溃肯定是由未处理异常引起的,你说的出了未处理异常但程序不奔溃是不可能的,

xmj112288 | 园豆:126 (初学一级) | 2015-11-12 12:02

@2727551894: 额,需要注意下,我的代码示例有using Timer = System.Windows.Timer;

我的timer不在ui线程执行的,timer不是控件。不是windows.form命名空间下的。

是System.Timers.Timer下的

你修改下timer的类型,然后从测试下。会有跨线程的异常出现的。

ChuckLu | 园豆:514 (小虾三级) | 2015-11-12 12:10

@ChuckLu: System.Windows.Timer 你这个命名空间是怎么引用的,我本地都找不到

 

而且 timer 不是控件的话,你的tick事件是怎么来的?

xmj112288 | 园豆:126 (初学一级) | 2015-11-13 10:16

@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 | 园豆:514 (小虾三级) | 2015-11-13 10:25

@ChuckLu: 

https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.100).aspx

你看Example上面的说明,这个事件里的异常会自动处理掉(大概意思),调试的时候,有调试器在,所以会抛出异常来。推荐你不要使用这个类,看说明很坑

xmj112288 | 园豆:126 (初学一级) | 2015-11-13 11:11

@ChuckLu: 补充一下,一般情况下只要有未处理的异常,程序就会奔溃的,除了这种特殊的Class

xmj112288 | 园豆:126 (初学一级) | 2015-11-13 11:12

@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.

ChuckLu | 园豆:514 (小虾三级) | 2015-11-13 11:15
其他回答(4)
0

你想要的崩溃的结果是什么?跳到源代码?还是弹出对话框提示异常?还是直接关机重起?

需要格局 | 园豆:2145 (老鸟四级) | 2015-11-10 10:58

之前程序有未处理的异常,然后程序就崩溃了。

和源代码无关的,直接运行的exe。

异常未处理,不需要弹出对话框。

和关机有什么关系?不懂。

 

正常的逻辑,应该是程序崩溃掉

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 11:01

@ChuckLu:我的意思是 你想要程序崩溃,你这个崩溃指得是什么?

是 这种 ?

 

还是这种:

支持(0) 反对(0) 需要格局 | 园豆:2145 (老鸟四级) | 2015-11-10 11:04

@田麦成: 对的,类似这种的崩溃。

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 11:06

@ChuckLu: 贴上你的代码……

支持(0) 反对(0) 需要格局 | 园豆:2145 (老鸟四级) | 2015-11-10 11:11

winform里面的异常看起来是这样的,程序直接崩溃掉了

 private void Form1_Load(object sender, EventArgs e)
        {
            Method();
        }
 private void Method()
        {
            int a = 1;
            int b = 0;
            int c = a / b;
        }

 

@ChuckLu: 

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 11:14

@田麦成: 

还有这种错误

        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;
        }

 

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 11:17

@ChuckLu:能看一下代码么?

支持(0) 反对(0) 需要格局 | 园豆:2145 (老鸟四级) | 2015-11-10 11:31

@田麦成: 代码已经上传了

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 13:01

@田麦成: 

  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;
        }

这个也会导致崩溃。

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 13:09

@ChuckLu:看不出来哪里错了

支持(0) 反对(0) 需要格局 | 园豆:2145 (老鸟四级) | 2015-11-10 15:40

@田麦成: System.Windows.Timer计时器是运行在其他线程上的,不是ui线程。

在这个上面对控件进行更新的话,调试模式下,会出现跨线程调用控件的异常提示。

但是编译成exe,打开运行之后,就会出问题

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 15:45

@ChuckLu:你给得只是只字片言的代码,我看不出来问题的。断章取义没办法的

支持(0) 反对(0) 需要格局 | 园豆:2145 (老鸟四级) | 2015-11-10 16:40

@田麦成: 只言片语?已经是完整的代码了呀。

winform上面,拖了一个label控件,控件名是label1.

代码已经非常的详尽了

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-10 16:50
0

forms.timer是运行在ui线程,timers.timer是多线程的,对于跨线程操作需要invoke。

看了上面的回复,你的意思是为什么线程里面的错误没有造成程序崩溃?

因为转web很久,对此不太了解,我的想法是,线程中的错误没有影响到UI线程,程序得以继续运行。

而调试的时候,IDE会帮助捕获一切错误。

上位者的怜悯 | 园豆:172 (初学一级) | 2015-11-11 16:39

之前别人给我讲解的时候,我也考虑到这个了。但是我后面新开一个线程或者在线程池上执行这一段代码。仍然会导致程序崩溃。上面倒数2个代码示例就演示这种情况。

在非ui线程触发了这个异常,但是程序崩溃了

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-11 16:46

@ChuckLu: 我想了一下,记得好像以前看了一篇文章,在2.0的时候是可以跨线程操作的。 我的想法:跨线程操作是可以的,但是如果2个线程同时操作一个控件可能会造成问题。于是IDE在调试的时候就检查这些不安全代码,并报错(此时不一定会真的出错了)。 ctrl+f5 直接运行的时候,这些不安全代码如果没有出问题,也就不报错了... 就像添加了CheckForIllegalCrossThreadCalls = false; 一样,对于跨线程访问控件不出问题就不管了。

支持(0) 反对(0) 上位者的怜悯 | 园豆:172 (初学一级) | 2015-11-11 17:08

@上位者的怜悯: 不明白。我没有加这个CheckForIllegalCrossThreadCalls = false; 就应该出错呀。

如果我在计时器里面添加try catch是会捕获到这个异常的。

说明异常肯定是发生了,但是没有导致程序崩溃。

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-11 17:21

@ChuckLu: 程序并没有出错,所以没有崩溃。【checkfor...=false】是让编辑器对这种跨线程访问的不安全代码不报错。  实际运行的时候,这种不安全代码如果没有出现问题也不会报错,如果真的有多个线程同时操作一个控件,就会崩溃了。   跨线程直接访问只是不安全,不一定会出错。

___________  个人见解。

支持(0) 反对(0) 上位者的怜悯 | 园豆:172 (初学一级) | 2015-11-11 17:38

@上位者的怜悯: 不是呀。你看其他的代码示例。程序崩溃,就是因为跨线程调用控件导致的

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-11 17:40

@ChuckLu: 调试的时候报错,是IDE帮你检测到了不安全代码,而实际上这代码运行起来不一定出错。

除以0 当然100%报错啊者不考虑了。

支持(0) 反对(0) 上位者的怜悯 | 园豆:172 (初学一级) | 2015-11-11 17:52

@上位者的怜悯: 除以0,程序貌似不会崩溃

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-11 19:07

@ChuckLu: 我解释不来,上面都是思考之后的想法,对于这个情况我只能这么解释。

除以0我没有测试,只试了ctrl+f5跨线程访问的。

不影响UI线程,或者没有真的出错,就不报错了。 不过这种说法我自己都信服不来,帮不了你。

支持(0) 反对(0) 上位者的怜悯 | 园豆:172 (初学一级) | 2015-11-11 19:45
0

主线程不崩溃 winform就不会崩溃

子线程影响不到主线程

至于子线程报错 调试程序是可以捕获到的 这个没有冲突

小眼睛老鼠 | 园豆:2731 (老鸟四级) | 2015-11-12 14:49
 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段代码也是子线程吧?这个就会导致程序崩溃。

支持(0) 反对(0) ChuckLu | 园豆:514 (小虾三级) | 2015-11-12 14:53
0

winfrom不太熟, 路过。

请叫我头头哥 | 园豆:9382 (大侠五级) | 2015-11-12 15:56
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册