正 文:
c#软件设计中,如果有多个线程同时地频繁地操作UI主线程同一个控件时,比如显示状态等,往往会出现“未将对象引用设置到对象的实例”错误。
有时即使你使用try...catch...来捕获错误依然无济于事,比如飘易最近的一个小开发应用。5个线程需要频繁的把状态写入到 RichTextBox 这个控件上,5个线程同时不停地写入数据,开始还好,但是运行一段时间后,就会出现“未将对象引用设置到对象的实例”错误,或者软件直接自动退出了。
这样的异常客户一定不满意。我猜测问题还是出现在多线程身上,即使加上了以下这句:
Control.CheckForIllegalCrossThreadCalls = false; 以便允许线程的不安全调用,.net内部还是会出现错误。
解决方法就是
使用字符串 StringBuilder类(string也可以,但是效率不如StringBuilder),临时缓存要显示的数据,然后用一个定时器,定时写入到RichTextBox这个控件上,这样就避免了频繁的操作同一个控件。
StringBuilder tmpv = new StringBuilder();
tmpv.AppendLine("...");
定时器间隔可以设为5秒,每隔5秒把tmpv里的数据显示到RichTextBox即可。
private void timer1_Tick(object sender, EventArgs e)
{//定时器
try
{
// 允许子线程更新UI线程里的控件
this.Invoke((EventHandler)delegate
{
richTextBox1.AppendText(tmpv.ToString());
tmpv.Length = 0; //清空StringBuilder
});
}
catch (Exception ){}
}
如有疑问,欢迎您留言。
List<T>非线程安全 在多线程里,尤其
要注意 List<T> 的使用,List不是线程安全的。List的Add以及List.ToArray等方法是线程不安全的! List的源码中的Add方法,使用了每次当当前的元素达到上限,通过创建一个新的数组实例,并给长度翻倍的操作。如果单线程操作不会有问题,直接扩容,然后继续往里面加值。当多个线程同时添加元素,且刚好它们都执行到了扩容这个阶段,当一个线程扩大了这个数组的长度,且进行了+1操作后,另外一个线程刚好也在执行扩容的操作,这个时候它给Capacity的值设为2048,但是另外一个线程已经将this._size设为2049了,所以这个时候就报异常了.当然不止这一个问题,还有Copy的时候也会出问题,如果里面的元素过多,另外一个线程拿到空值的几率很大。
如果不注意处理,就会出现错误:
在 System.Windows.Forms.RichTextBox.EditStreamProc(IntPtr dwCookie, IntPtr buf, Int32 cb, Int32& transferred)
在 System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
在 System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
在 System.Windows.Forms.Control.WndProc(Message& m)
在 System.Windows.Forms.RichTextBox.WndProc(Message& m)
在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
在 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
怎么解决呢?解决方案:
1.扩容List的初始容量为集合需要的实际容量或更大;
2.给List.Add等方法加锁;
3.使用List的线程安全版本。
下来飘易来介绍下加锁 lock 的方案:
private static readonly Object Locker = new Object();
List<string> logList = new List<string>();
lock (Locker)
{
string cs = string.Join("\r\n", logList.ToArray());
logList.Add("...");
}
【参考】:
*
C# 多线程之List的线程安全问题