线程间操作无效: 从不是创建控件“WaitForm”的线程访问它

本文详细阐述了一个Winform应用中出现的线程间访问冲突问题,导致线程终止异常。通过分析错误日志,发现是由于在主程序调用关闭方法时,等待提示框还未完全渲染,从而引发异常。文中提出了两种解决方案,并最终通过调整Winform对象的访问限制属性,成功解决了问题。
摘要由CSDN通过智能技术生成

这个错误提示导致我花了一天半的时间来思考为什么会有这个问题..

以前只明白线程的作用和意义...但是在用的时候却没怎么考虑过..只知道用...

我这个项目是winform的,需要用到webservices远程调用..

调用远程webservices的时候速度比较慢...程序处于死机状态...

客户不满意,让加个进度条...

自己觉得这没啥难得,自己写个winform窗体,直接show一下就行了...

.....一翻代码敲下来... 运行...

蹬蹬..澄澄,,, 没反应..

等待窗口都没出来....

跟没写差不多.....

根据我以往的习惯..这种小控件...网上肯定有现成的...

于是网上百度了一个进度条...

效果还不错....


运行了一下...还行...自己F5运行试了几次都没问题...

于是发布了...

程序到了客户手里..几下就挂了...而且不是每次必挂....可重现性比较小...

出来一个莫名其妙的问题...


看的我稀里糊涂....


详细错误内容如下:


See the end of this message for details on invoking 
just-in-time (JIT) debugging instead of this dialog box.


************** Exception Text **************
System.Threading.ThreadAbortException: Thread was being aborted.
   at System.Drawing.SafeNativeMethods.BitBlt(HandleRef hDC, Int32 x, Int32 y, Int32 nWidth, Int32 nHeight, HandleRef hSrcDC, Int32 xSrc, Int32 ySrc, Int32 dwRop)
   at System.Drawing.BufferedGraphics.RenderInternal(HandleRef refTargetDC, BufferedGraphics buffer)
   at System.Drawing.BufferedGraphics.Render()
   at System.Windows.Forms.Control.WmPaint(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)



这个错误把我看的.真是稀里糊涂... 

我所有的代码都try catch过了.结果却捕获不到这个错误...

一头雾水... 

经过详细的排查,最终把问题锁定到这个百度到的等待提示框上..

然后自己分析,和查资料...

根据这个错误可以看出来是一个

ThreadAbortException 线程终止异常..

然后它的代码是这样的...


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace GateAutoPrint
{
    /// <summary>
    /// Using Singleton Design Pattern
    /// </summary>
    public class WaitFormService
    {

        /// <summary>
        /// 单例模式
        /// </summary>
        public static WaitFormService Instance
        {
            get
            {
                if (WaitFormService._instance == null)
                {
                    lock (syncLock)
                    {
                        if (WaitFormService._instance == null)
                        {
                            WaitFormService._instance = new WaitFormService();
                        }
                    }
                }
                return WaitFormService._instance;
            }
        }

        /// <summary>
        /// 为了单例模式防止new 实例化..
        /// </summary>
        private WaitFormService()
        {
        }


        private static WaitFormService _instance;
        private static readonly Object syncLock = new Object();
        private Thread waitThread;
        private static WaitForm waitForm; 
        /// <summary>
        /// 显示等待窗体
        /// </summary>
        public static void Show()
        {
           
            //try
            //{
                WaitFormService.Instance._CreateForm(); 
                //isclose = false;
            //}
            //catch (Exception ex)
            //{ 
            //}
        }

        /// <summary>
        /// 关闭等待窗体
        /// </summary>
        public static void Close()
        {

            //if (isclose == true)
            //{
            //    return;
            //}
            //try
            //{
                WaitFormService.Instance._CloseForm(); 
                //isclose = true;
            //}
            //catch (Exception ex)
            //{ 
            //}
        }

        /// <summary>
        /// 设置等待窗体标题
        /// </summary>
        /// <param name="text"></param>
        public static void SetText(string text)
        {
            //if (isclose == true)
            //{
            //    return;
            //}
            //try
            //{
                WaitFormService.Instance.SetWaiteText(text);
            //}
            //catch (Exception ex)
            //{ 
            //}
        }


        /// <summary>
        /// 创建等待窗体
        /// </summary>
        public void _CreateForm()
        { 
            waitForm = null;
            waitThread = new Thread(new ThreadStart(this._ShowWaitForm));
            waitThread.Start();
        }

        private void _ShowWaitForm()
        {
            try
            {
                waitForm = new WaitForm();
                waitForm.ShowDialog();
                //Application.Run(waitForm);
            }
            catch (ThreadAbortException e)
            {
                waitForm.Close();
                Thread.ResetAbort();
            }
        }

        /// <summary>
        /// 关闭窗体
        /// </summary>
        private void _CloseForm()
        {
            
            //waitForm.Close();
            //waitForm = null;
            if (waitThread != null)
            {
                waitThread.Abort();
            }
        }
         

        /// <summary>
        /// 设置窗体标题
        /// </summary>
        /// <param name="text"></param>
        public void SetWaiteText(string text)
        {
            if (waitForm != null)
            {
                //try
                //{
                    waitForm.Show();
                    waitForm.SetText(text);
                //}
                //catch (Exception ex)
                //{ 
                //}
            }
        }

    }
}



根据异常内容我猜测肯定是窗口在渲染的时候,还没来得及显示出来..主程序已经开始调用.close了.

而这个close中的关闭方法是直接把

  waitThread.Abort();
强制终止掉... 

所以报这个异常了...


后来想对策如下几种:

1.不强制终止线程..让winform自己close()掉..然后子线程就会关闭掉..

2.强制终止线程,,,但是捕获异常...把异常给闷掉...

先尝试了第二套方案..... 

异常捕获了然后也闷掉了... 结果还是出错..看样要想捕获另外一个线程中的异常还真是件麻烦事.... 

这种方案肯定不是最好的....否定了

 然后尝试第一套方案.

第一套方案,要想让winform自己close还不方便..

直接把

waitThread.Abort();
改成
waitform.close();
结果很悲局, 
出现了如题所属的异常.
                   线程间操作无效: 从不是创建控件“WaitForm”的线程访问它

这是为啥啊?不都是waitform吗? 为啥就不能访问呢??

难道说...线程与线程之间不能互相访问变量吗?

又是一番查资料...
结果证实,普通的string int变量能够通用..在任何线程中访问都没问题...

但是为什么winform 就不行呢??

又是一番百度谷歌...

结果证实...在winform中把一个属性更改以后,线程之间就可以随意访问winform对象了..

这个属性就是

CheckForIllegalCrossThreadCalls 属性设置为 false 如下所示:

 public Form1()
        {
            InitializeComponent();
            //成功
            CheckForIllegalCrossThreadCalls = false;
        }


这个属性的本来是没有的..微软工程师在2.0以后加进去的这个属性,为了使得我们这些小罗罗不要犯无知的错误...

无视多线程的潜在危险就是无视自己的无知...


实际上...多线程就像多个人在下象棋,,象棋棋子是可以共享的.大家都能操作...

但为什么winform窗体就不行呢? 

实际上winform窗体本来也是个变量也是个棋子...

但是微软的工程师给他们加了个限制条件...例如"士"只能走斜线.. "将"不能出大营..

为的就是少犯错误..

如果你的应用程序真的不怕多个线程同时访问一个winform.窗体..

那你尽管

CheckForIllegalCrossThreadCalls = false; 





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值