这个错误提示导致我花了一天半的时间来思考为什么会有这个问题..
以前只明白线程的作用和意义...但是在用的时候却没怎么考虑过..只知道用...
我这个项目是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;
}
无视多线程的潜在危险就是无视自己的无知...
实际上...多线程就像多个人在下象棋,,象棋棋子是可以共享的.大家都能操作...
但为什么winform窗体就不行呢?
实际上winform窗体本来也是个变量也是个棋子...
但是微软的工程师给他们加了个限制条件...例如"士"只能走斜线.. "将"不能出大营..
为的就是少犯错误..
如果你的应用程序真的不怕多个线程同时访问一个winform.窗体..
那你尽管
CheckForIllegalCrossThreadCalls = false;