來源:http://blog.csdn.net/jinjazz/archive/2007/12/10/1927126.aspx
CodeProject上有篇文章An Alternate Way of Writing a Multithreaded GUI in C#
本意是Alternate Way 另外一種方法,後來莫名其妙的被轉載成中文變了題目,最高效的方法。
CheckForIllegalCrossThreadCalls和control.Invoke有什麼不同,哪個更好用,更高效呢?
佔在任何角度講,都不要使用CheckForIllegalCrossThreadCalls,即便他運行和代碼編寫的確實比Invoke效率高 。
感興趣的,可以參考我後面貼出的代碼來測試對比一下兩者的不同。這裡我只簡單說一下結論:
1、效能CheckForIllegalCrossThreadCalls=false時比invoke高,而且代碼比較優雅。
測試時間如下:
Button1 ---------------------------
00:00:01.0760900
00:00:01.0771200
Button2 --------------------------
00:00:01.0812499
00:00:01.0813443
效率差多少?在這裡時間還不到1%,代碼少寫一個if字句
看到有文章說這種方法在大量更新ui時會引發大量異常,導致效能下降
我測試了一下,耗時和迴圈次數是很平穩的線性關係,而且也沒有發現幾個Exception相關效能計數器有問題,這說明這又是某老外朋友想當然的說法。
2、CheckForIllegalCrossThreadCalls在.net1.x中預設是false,也就是不檢查,.net2.0和3.x預設是true
說明這是ms有意的引導,說不定以後不讓你改了。這也是很多1.x使用者在剛用2.0時不習慣跨線程更新ui的原因之一。
3、死穴:安全性
CheckForIllegalCrossThreadCalls容許子線呈隨時更新ui,在同一個test函數體內,不能保證自身事務的一致性。給label1付了值
一回頭,就已經被別人改了,這和超市的踩踏事件的後果一樣嚴重。
當然你可以自己加鎖,用訊號量,這樣還不如直接使用Invoke了,你只是又把別人做好的事情做了一遍。
如果你覺的你的應用不會考慮在寫入ui的同時來讀取ui,而傾向使用CheckForIllegalCrossThreadCalls來追求效率的話,也是不恰當的做法。
首先CheckForIllegalCrossThreadCalls並不能讓效率發生本質的變化。
其次需求永遠是變化的,現在不考慮不等於以後不會碰到
聽從ms的引導。否則以後要在高版本的.net framework中移植代碼的時候需要花費數倍的人工。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
publicpartialclass Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// 類比一個實際應用
// 對label1付值後立馬檢查他的值,如果已經被改過則拋出異常
void test()
{
string strText = Guid.NewGuid().ToString();
this.label1.Text = strText;
}
// 不使用invoke方法直接進入Control
void doThread()
{
System.Diagnostics.Stopwatch sw =new System.Diagnostics.Stopwatch();
sw.Reset();
sw.Start();
for (int i =0; i <100; i++)
{
test();
System.Threading.Thread.Sleep(10);
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
}
// 使用invoke方法
publicdelegatevoid dTest();
void invokeThread()
{
System.Diagnostics.Stopwatch sw =new System.Diagnostics.Stopwatch();
sw.Reset();
sw.Start();
for (int i =0; i <100; i++)
{
if (this.InvokeRequired)
{
this.Invoke(new dTest(test));
}
else
{
test();
}
System.Threading.Thread.Sleep(10);
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
}
// 通過CheckForIllegalCrossThreadCalls的值忽略跨線程錯誤,這時會拋出異常
privatevoid button1_Click(object sender, EventArgs e)
{
Form1.CheckForIllegalCrossThreadCalls =false;
new System.Threading.Thread(new System.Threading.ThreadStart(doThread)).Start();
new System.Threading.Thread(new System.Threading.ThreadStart(doThread)).Start();
}
// 通過invoke方法,在主線程排隊,lable1的值在test函數體內永遠是安全執行緒的.
privatevoid button2_Click(object sender, EventArgs e)
{
new System.Threading.Thread(new System.Threading.ThreadStart(invokeThread)).Start();
new System.Threading.Thread(new System.Threading.ThreadStart(invokeThread)).Start();
}
}
}