C# WinForm多線程開發(二) ThreadPool 與 Timer
原文地址:點擊打開鏈接
[摘要]本文介紹C# WinForm多線程開發之ThreadPool 與 Timer,並提供詳細的示例代碼供參考。
本文接上文,繼續探討WinForm中的多線程問題,再次主要探討threadpool 和timer。
一 、ThreadPool
線程池(ThreadPool)是一種相對較簡單的方法,它適應於一些需要多個線程而又較短任務(如一些常處於阻塞狀態的線程),它的缺點是對創建的線程不能加以控製,也不能設置其優先級。由於每個進程隻有一個線程池,當然每個應用程序域也隻有一個線程池(對線),所以你將發現 ThreadPool類的成員函數都為static!當你首次調用ThreadPool.QueueUserWorkItem、 ThreadPool.RegisterWaitForSingleObject等,便會創建線程池實例。下麵我就線程池當中的兩函數作一介紹:
public static bool QueueUserWorkItem( //調用成功則返回true WaitCallback callBack,//要創建的線程調用的委托 object state //傳遞給委托的參數 )//它的另一個重載函數類似,隻是委托不帶參數而已此函數的作用是把要創建的線程排隊到線程池,當線程池的可用線程數不為零時(線程池有創建線程數的限製,缺身值為25),便創建此線程,否則就排隊到線程池等到它有可用的線程時才創建。
public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,// 要注冊的 WaitHandle
WaitOrTimerCallback callBack,// 線程調用的委托
object state,//傳遞給委托的參數
int TimeOut,//超時,單位為毫秒,
bool executeOnlyOnce //是否隻執行一次
);
public delegate void WaitOrTimerCallback(
object state,//也即傳遞給委托的參數
bool timedOut//true表示由於超時調用,反之則因為waitObject
);
此函數的作用是創建一個等待線程,一旦調用此函數便創建此線程,在參數waitObject變為終止狀態或所設定的時間TimeOut到了之前,它都處於 “阻塞”狀態,值得注意的一點是此“阻塞”與Thread的WaitSleepJoin狀態有很大的不同:當某Thread處於 WaitSleepJoin狀態時CPU會定期的喚醒它以輪詢更新狀態信息,然後再次進入WaitSleepJoin狀態,線程的切換可是很費資源的;而用此函數創建的線程則不同,在觸發它運行之前,CPU不會切換到此線程,它既不占用CPU的時間又不浪費線程切換時間,但CPU又如何知道何時運行它?實際上線程池會生成一些輔助線程用來監視這些觸發條件,一旦達到條件便啟動相應的線程,當然這些輔助線程本身也占用時間,但是如果你需創建較多的等待線程時,使用線程池的優勢就越加明顯。
更詳細內容demo:
namespace TestMethodInvoker
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//ThreadPool.RegisterWaitForSingleObject(
// ev,
// new WaitOrTimerCallback(WaitThreadFunc),
// 4,
// 2000,
// false//表示每次完成等待操作後都重置計時器,直到注銷等待
// );
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadFunc), "test1");
//Thread.Sleep(10000);
}
private delegate void MyInvokeDelegate(string name);
private void Test(object o)
{
richTextBox1.Text += string.Format("the object is {0} \n", o);
}
public void ThreadFunc(object b)
{
this.Invoke(new MyInvokeDelegate(Test), b);
}
public void WaitThreadFunc(object b, bool t)
{
richTextBox1.Text += string.Format("the object is {0},t is {1}\n", b, t);
}
}
}
一個很值得擴展的地方時,這裏的invoke 用的是代理,其實還有其他的方法,比如 action 和func。實例代碼如下:
this.Invoke(new Action<string>(this.ChangeText), o.ToString());
this.Invoke(new Action(delegate() { this.textBox1.Text = o.ToString();}));
private void DoSomething(object o)
{
System.Func<string, int> f = new Func<string, int>(this.GetId);
object result = this.Invoke(f, o.ToString());
MessageBox.Show(result.ToString());
}
private int GetId(string name)
{
this.textBox1.Text = name;
if (name == "Y")
{
return 999;
}
else
{
return 0;
}
}
二、 Timer
它適用於需周期性調用的方法,它不在創建計時器的線程中運行,它在由係統自動分配的單獨線程中運行。這和Win32中的SetTimer方法類似。它的構造為:
public Timer(
TimerCallback callback,//所需調用的方法
object state,//傳遞給callback的參數
int dueTime,//多久後開始調用callback
int period//調用此方法的時間間隔
);//
如果 dueTime 為0,則 callback 立即執行它的首次調用。如果 dueTime 為 Infinite,則 callback 不調用它的方法。計時器被禁用,但使用 Change 方法可以重新啟用它。如果 period 為0或 Infinite,並且 dueTime 不為 Infinite,則 callback 調用它的方法一次。計時器的定期行為被禁用,但使用 Change 方法可以重新啟用它。如果 period 為零 (0) 或 Infinite,並且 dueTime 不為 Infinite,則 callback 調用它的方法一次。計時器的定期行為被禁用,但使用 Change 方法可以重新啟用它。
在創建計時器之後若想改變它的period和dueTime,我們可以通過調用Timer的Change方法來改變:
public bool Change(
int dueTime,
int period
);//
顯然所改變的兩個參數對應於Timer中的兩參數。最後更新:2017-04-04 07:33:17