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.Text;

using System.Windows.Forms;


namespace WindowsApplication34


...{

public partial class Form1 : Form


...{

public Form1()


...{

InitializeComponent();

}




//模擬一個實際應用

//對label1付值後立馬檢查他的值,如果已經被改過則拋出異常


void test()


...{

string strText = Guid.NewGuid().ToString();

this.label1.Text = strText;

if (this.label1.Text != strText)


...{

//測試性能時把這一句注釋掉!!!!!!!!!!!!!!!!!!!!!!1

throw new Exception("label1的值意外改變");

}

}


//不使用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方法

public delegate void 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的值忽略跨線程錯誤,這時會拋出異常

private void 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函數體內永遠是線程安全的.

private void 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();

}

}

}