none
子线程Invoke了UI线程操作,导致子线程不能正常结束了 RRS feed

  • 问题

  • 问题描述:

    UI线程里启动了A,B子线程;

    A,B子线程均含有一个while(true){}的循环,循环体是子线程的主要代码;

    while(true)
    {
      if(status==status.stop)
      {
        break;
      }
      else
      {
        //执行正常操作
      }
    }
    其中B子线程是while(true){...UI.Control.Invoke(UI线程操作方法)},也就是说B子线程在正常执行代码里,要循环调用UI线程控件的Invoke()去更新UI。

    A,B子线程的结束均是通过判断一个变量值status是否结束。

    碰到的问题描述:

    我在UI线程里,设定status=status.stop后,发现子线程A很快跳出循环,结束了;但是B子线程一直都不结束;

    但是我想B子线程每次循环执行一次短暂的UI操作,循环完成后都会判断status,是否跳出循环,不应该等了很久(20秒)都还没有结束线程啊。

    其他测试:我在B子线程里,把代码修改成了UI.Control.BeginInvoke()的话,B子线程就可以很快结束。

    请问B子线程一直都不结束,是怎么回事?

    2013年9月1日 16:54

答案

  • 有可能会有异常,不过在vs调试环境中,也没有抛出异常来啊。

    如果你人为开创了一个线程(比如用Thread,或者更高的ThreadPool又或者Task),如果在里边直接访问UI的控件,调试的时候会抛出异常,但是直接Ctrl+F5不会,原理在于该异常不是UI线程(即主线程)的异常,自然不会显示。

    至于说你的B线程为何执行之后无法立即结束,这是因为多个线程访问同一个方法的时候可能都会保留该变量的一个副本,所以给出的建议是:

    1)把status定义为volitale

    2)在代码后面增加一句:

    while(true)
    {
    Thread.Sleep(1);
    if(status==status.stop) { break; } else { //执行正常操作 } }

    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    2013年9月2日 3:45
    版主
  • hello,

    你得设定中断观察到底哪里发生错误

    按下你的 UI  Stop 后,在按下 VS 的中断按钮


    然后利用 Threads 视窗观察线程运行状态,主要是B线程


    线程无法如预其中止有很多的原因,可能是

    1.线程互锁
    2.锁未释放
    3.逻辑错误

    提醒:若在B线程更新UI的速度太快,在关闭UI的时,可能会跳出 ObjectDisposedException 例外,你可以捕捉此例外



    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    2013年9月2日 5:00
  • 在非UI线程中调用 Control.Invoke 可能会出异常。


    有可能会有异常,不过在vs调试环境中,也没有抛出异常来啊。

    “调试”-〉“异常”,然后把“引发”全勾上。
    2013年9月2日 5:19
  • 有可能会有异常,不过在vs调试环境中,也没有抛出异常来啊。

    如果你人为开创了一个线程(比如用Thread,或者更高的ThreadPool又或者Task),如果在里边直接访问UI的控件,调试的时候会抛出异常,但是直接Ctrl+F5不会,原理在于该异常不是UI线程(即主线程)的异常,自然不会显示。

    至于说你的B线程为何执行之后无法立即结束,这是因为多个线程访问同一个方法的时候可能都会保留该变量的一个副本,所以给出的建议是:

    1)把status定义为volitale

    2)在代码后面增加一句:

    while(true)
    {
    Thread.Sleep(1); if(status==status.stop) { break; } else { //执行正常操作 } }

    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    这个是正解,还有什么不懂可以看看这个文章:http://msdn.microsoft.com/zh-cn/library/7a2f3ay4(v=vs.90).aspx

    If my post is helpful,please help to vote as helpful, if my post solve your question, please help to make it as answer. My sample

    2013年9月3日 8:56
  • hello,

    请试着捕捉应用程式等级的例外

    http://www.dotblogs.com.tw/yc421206/archive/2010/07/12/16499.aspx


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    2013年9月5日 10:01

全部回复

  • Invoke执行后需要等待方法完成,而BeginInvoke不需要http://stackoverflow.com/questions/1909839/invoke-and-begininvoke

    http://feiyun0112.cnblogs.com/

    2013年9月2日 1:11
    版主
  • Invoke执行后需要等待方法完成,而BeginInvoke不需要http://stackoverflow.com/questions/1909839/invoke-and-begininvoke

    http://feiyun0112.cnblogs.com/

    Invoke执行后需要等待我知道。

    Invoke执行方法里是一个简单的GDI+绘图操作,执行速度不会太慢,我已经在UI线程等待了10秒,查看子线程B还是alive状态的。

    我试试看是不是Invoke方法在结束阶段时,由于变量的问题,导致Invoke执行出错了

    2013年9月2日 1:24
  • 在非UI线程中调用 Control.Invoke 可能会出异常。

    2013年9月2日 2:24
  • 在非UI线程中调用 Control.Invoke 可能会出异常。


    有可能会有异常,不过在vs调试环境中,也没有抛出异常来啊。
    2013年9月2日 3:30
  • 有可能会有异常,不过在vs调试环境中,也没有抛出异常来啊。

    如果你人为开创了一个线程(比如用Thread,或者更高的ThreadPool又或者Task),如果在里边直接访问UI的控件,调试的时候会抛出异常,但是直接Ctrl+F5不会,原理在于该异常不是UI线程(即主线程)的异常,自然不会显示。

    至于说你的B线程为何执行之后无法立即结束,这是因为多个线程访问同一个方法的时候可能都会保留该变量的一个副本,所以给出的建议是:

    1)把status定义为volitale

    2)在代码后面增加一句:

    while(true)
    {
    Thread.Sleep(1);
    if(status==status.stop) { break; } else { //执行正常操作 } }

    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    2013年9月2日 3:45
    版主
  • hello,

    你得设定中断观察到底哪里发生错误

    按下你的 UI  Stop 后,在按下 VS 的中断按钮


    然后利用 Threads 视窗观察线程运行状态,主要是B线程


    线程无法如预其中止有很多的原因,可能是

    1.线程互锁
    2.锁未释放
    3.逻辑错误

    提醒:若在B线程更新UI的速度太快,在关闭UI的时,可能会跳出 ObjectDisposedException 例外,你可以捕捉此例外



    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    2013年9月2日 5:00
  • 在非UI线程中调用 Control.Invoke 可能会出异常。


    有可能会有异常,不过在vs调试环境中,也没有抛出异常来啊。

    “调试”-〉“异常”,然后把“引发”全勾上。
    2013年9月2日 5:19
  • hello,

    你得设定中断观察到底哪里发生错误

    按下你的 UI  Stop 后,在按下 VS 的中断按钮


    然后利用 Threads 视窗观察线程运行状态,主要是B线程


    线程无法如预其中止有很多的原因,可能是

    1.线程互锁
    2.锁未释放
    3.逻辑错误

    提醒:若在B线程更新UI的速度太快,在关闭UI的时,可能会跳出 ObjectDisposedException 例外,你可以捕捉此例外



    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    谢谢

    我没有加锁,因为只有UI线程写变量status,子线程A,B都只是读取status,并且对同步没必须的要求。

    也没关闭UI,只是子线程B,一但读取到status==status.stop时,就跳出循环了,跳出循环这个不涉及到UI.Control.Invoke工作。

    2013年9月2日 8:41
  • status=status.stop当然会跳出循环啊,这是你代码逻辑所致。

    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    2013年9月2日 8:54
    版主
  • status=status.stop当然会跳出循环啊,这是你代码逻辑所致。

    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    我意思是:跳出循环了,就应该结束线程,不过现在线程没能退出来。

    我得想想是啥原因去。可能是UI.Invoke的原因,我还没来得及试呢

    2013年9月2日 9:16
  • break如果进入了,循环肯定退出了。

    你所谓“线程”没有退出,啥意思?


    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    2013年9月2日 9:26
    版主
  • 你开启线程B的代码是怎样的? 

    If my post is helpful,please help to vote as helpful, if my post solve your question, please help to make it as answer. My sample

    2013年9月3日 7:01
  • 你开启线程B的代码是怎样的? 

    If my post is helpful,please help to vote as helpful, if my post solve your question, please help to make it as answer. My sample


    纯粹用Thread.Start()开始的一个线程,也没有传入参数。
    2013年9月3日 8:26
  • break如果进入了,循环肯定退出了。

    你所谓“线程”没有退出,啥意思?


    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    我自己调试时,看见B子线程,不进break语句。可明明status已经是等于status.stop了。while循环里一直判断这个状态值,如果等于stop就break。

    我想就算是UI.Control.Invoke()操作慢,那我等一会也总会执行完吧,执行完一次Invoke方法后,就会进入下次循环,开始判断status值。但是等了好久都没有见循环进入break。如果Invoke执行不过去,那么有异常就能进入异常吧,不过也没进去异常中断。Invoke方法不复杂,没有死锁死循环什么的。

    注释掉Invoke代码后,子线程B就肯定能正确退出,我现在试试看Invoke方法哪个地方不对了。

    2013年9月3日 8:30
  • break如果进入了,循环肯定退出了。

    你所谓“线程”没有退出,啥意思?


    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    我自己调试时,看见B子线程,不进break语句。可明明status已经是等于status.stop了。while循环里一直判断这个状态值,如果等于stop就break。

    我想就算是UI.Control.Invoke()操作慢,那我等一会也总会执行完吧,执行完一次Invoke方法后,就会进入下次循环,开始判断status值。但是等了好久都没有见循环进入break。如果Invoke执行不过去,那么有异常就能进入异常吧,不过也没进去异常中断。Invoke方法不复杂,没有死锁死循环什么的。

    注释掉Invoke代码后,子线程B就肯定能正确退出,我现在试试看Invoke方法哪个地方不对了。

    1、把 status 申明为 voliate;

    2、try { UI.Control.Invoke ;} catch()

    2013年9月3日 8:35
  • 有可能会有异常,不过在vs调试环境中,也没有抛出异常来啊。

    如果你人为开创了一个线程(比如用Thread,或者更高的ThreadPool又或者Task),如果在里边直接访问UI的控件,调试的时候会抛出异常,但是直接Ctrl+F5不会,原理在于该异常不是UI线程(即主线程)的异常,自然不会显示。

    至于说你的B线程为何执行之后无法立即结束,这是因为多个线程访问同一个方法的时候可能都会保留该变量的一个副本,所以给出的建议是:

    1)把status定义为volitale

    2)在代码后面增加一句:

    while(true)
    {
    Thread.Sleep(1); if(status==status.stop) { break; } else { //执行正常操作 } }

    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    这个是正解,还有什么不懂可以看看这个文章:http://msdn.microsoft.com/zh-cn/library/7a2f3ay4(v=vs.90).aspx

    If my post is helpful,please help to vote as helpful, if my post solve your question, please help to make it as answer. My sample

    2013年9月3日 8:56
  • hello,

    我无法模拟您的错误,烦请您提供您的代码,以便解决问题

    private void button_Start_Click(object sender, EventArgs e)
    {
        if (this._isStart)
        {
            return;
        }
        this._isStart = true;
        var t1 = new Thread(process);
        var t2 = new Thread(update);
        t1.Start();
        t2.Start();
        //ThreadPool.QueueUserWorkItem(_ => process());
        //ThreadPool.QueueUserWorkItem(_ => update());
    }
    
    private void process()
    {
        while (true)
        {
            if (!this._isStart)
            {
                break;
            }
            this._result++;
            SpinWait.SpinUntil(() => false, 1);
        }
    }
    
    private void update()
    {
        while (true)
        {
            if (!this._isStart)
            {
                break;
            }
    
            this.Invoke((Action)(() =>
            {
                this.label_Result.Text = this._result.ToString();
            }));
    
            SpinWait.SpinUntil(() => false, 1);
        }
    }
    
    private void button_Stop_Click(object sender, EventArgs e)
    {
        if (!this._isStart)
        {
            return;
        }
        this._isStart = false;
    }
    

    工作中的线程状态

    当我按下 Stop 没有线程在工作


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    2013年9月3日 10:00
  • hello,

    我无法模拟您的错误,烦请您提供您的代码,以便解决问题

    private void button_Start_Click(object sender, EventArgs e)
    {
        if (this._isStart)
        {
            return;
        }
        this._isStart = true;
        var t1 = new Thread(process);
        var t2 = new Thread(update);
        t1.Start();
        t2.Start();
        //ThreadPool.QueueUserWorkItem(_ => process());
        //ThreadPool.QueueUserWorkItem(_ => update());
    }
    
    private void process()
    {
        while (true)
        {
            if (!this._isStart)
            {
                break;
            }
            this._result++;
            SpinWait.SpinUntil(() => false, 1);
        }
    }
    
    private void update()
    {
        while (true)
        {
            if (!this._isStart)
            {
                break;
            }
    
            this.Invoke((Action)(() =>
            {
                this.label_Result.Text = this._result.ToString();
            }));
    
            SpinWait.SpinUntil(() => false, 1);
        }
    }
    
    private void button_Stop_Click(object sender, EventArgs e)
    {
        if (!this._isStart)
        {
            return;
        }
        this._isStart = false;
    }

    工作中的线程状态

    当我按下 Stop 没有线程在工作


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    非常感谢你。谢谢你的提示。

    代码贴出来是不是行数太多了?

    我先自己慢慢查一查代码看看先


    2013年9月4日 15:59
  • 谢谢大家的帮忙,我总算查到问题所在了。确实是UI.Control.Invoke()过程出错了。里边数组读取的时候出界了。

    不过好奇怪,vs调试的时候,没能进入调试界面,可能是因为是子线程里Invoke的UI线程出错的原因吧。是不是只有UI线程异常,vs才能自己停下来?

    2013年9月5日 9:01
  • 谢谢大家的帮忙,我总算查到问题所在了。确实是UI.Control.Invoke()过程出错了。里边数组读取的时候出界了。

    不过好奇怪,vs调试的时候,没能进入调试界面,可能是因为是子线程里Invoke的UI线程出错的原因吧。是不是只有UI线程异常,vs才能自己停下来?

    观察你的“输出”窗口,会有异常提示。

    “调试”-〉“异常”,然后把“引发”全勾上。

    2013年9月5日 9:03
  • hello,

    请试着捕捉应用程式等级的例外

    http://www.dotblogs.com.tw/yc421206/archive/2010/07/12/16499.aspx


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    2013年9月5日 10:01