【主執行緒要等副執行緒查完之後才會更新介面上的資訊感覺很鳥】,
所以今天我要來改善這個問題,
基本上我想到的解法就是把原本查詢按鈕按下去裡面的程式改成另一個獨立的函式,
然後用副執行緒去呼叫,
所以原來的 btnThreadingQuery_Click 就變成下面這樣啦!
private void
btnThreadingQuery_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(QueryMain));
}
原來裡面的程式就被我移到了 QueryMain() 去了,然後妳還記得嗎?
在第一篇文章(C# 多緒程式該怎麼寫? - Threading)的時候,
我曾經說過副執行緒不可以直接去處理 UI 相關的東西,
所以原本我們在裡面有去控制按鈕跟滑鼠游標也要依照第一篇的觀念去修改,
全部的修改內容如下:
private void
QueryMain(object param)
{
waitHandles = new List<WaitHandle>();
Cursor tmpCursor = this.Cursor;
Label[] labels = {
lblThreadingDuration, lblThreadingTable1Count,
lblThreadingTable1Duration, lblThreadingTable2Count,
lblThreadingTable2Duration };
ResetLabel(labels);
//Application.DoEvents();
try
{
//btnThreadingQuery.Enabled
= false;
this.Invoke(new UpdateButtonEnableHandler(UpdateButtonEnable),
new
object[] { btnThreadingQuery, false });
//this.Cursor
= Cursors.WaitCursor;
this.Invoke(new UpdateFormCursorHandler(UpdateFormCursor),
new
object[] { Cursors.WaitCursor
});
CalcTime
calcTime = new CalcTime();
waitHandles.Add(new AutoResetEvent(false));
object
param1 = new object[]
{ "table1",
lblThreadingTable1Count,
lblThreadingTable1Duration,
calcTime, waitHandles[waitHandles.Count()-1]};
ThreadPool.QueueUserWorkItem(
new
WaitCallback(QueryTableByThreading),
param1);
waitHandles.Add(new AutoResetEvent(false));
object
param2 = new object[]
{ "table2",
lblThreadingTable2Count,
lblThreadingTable2Duration, calcTime,
waitHandles[waitHandles.Count()-1]};
ThreadPool.QueueUserWorkItem(
new
WaitCallback(QueryTableByThreading),
param2);
WaitHandle.WaitAll(waitHandles.ToArray());
//lblThreadingDuration.Text
= calcTime.Duration();
this.Invoke(new UpdateLabelTextHandler(UpdateLabelText),
new
object[] { lblThreadingDuration,
calcTime.Duration() });
}
finally
{
//this.Cursor
= tmpCursor;
this.Invoke(new UpdateFormCursorHandler(UpdateFormCursor),
new
object[] { tmpCursor });
//btnThreadingQuery.Enabled
= true;
this.Invoke(new UpdateButtonEnableHandler(UpdateButtonEnable),
new
object[] { btnThreadingQuery, true });
}
}
所以函式就一定要長得跟它的規定一樣,
所以我們宣告的函式就要長成這樣(如下),
private void QueryMain(object param)
即便我們沒有要傳遞任何參數也是一樣的。
裡面的內容我們修改的不多,
基本上就是有直接異動 UI 的程式我們改成用 this.Invoke 來處理;
所以原本的按鈕跟滑鼠游標就需要修改了。
修改如下:
delegate void
UpdateButtonEnableHandler(Button btn, bool
enable);
private void
UpdateButtonEnable(Button btn, bool enable)
{
btn.Enabled = enable;
}
delegate void
UpdateFormCursorHandler(Cursor c);
private void
UpdateFormCursor(Cursor c)
{
this.Cursor = c;
}
另一個要修改的就是把 Label 初始的那個程式,
原本直接把 Label.Text 指定成 "0" 就要變成用間接的方式去改了,
如下:
private void
ResetLabel(Label[] labels)
{
foreach (Label
lb in labels)
{
//lb.Text =
"0";
this.Invoke(new UpdateLabelTextHandler(UpdateLabelText),
new
object[] { lb, "0"
});
}
}
這樣全部程式就已經都改完了,
感覺是不是很簡單呢。
忘了說,這時候我們已經可以把 QueryTableByThreading 裡面的 are.Set(); 重新移到程式的最末端去了,
現在已經不會發生程式卡住的問題了,
至於原因是甚麼,妳就自己好好想想囉!
現在程式執行起來就跟我想要的一模一樣了,
查詢完 Table2 之後就會先顯示資料筆數跟時間在畫面上了,
而這時候按鈕還是不能按,滑鼠也還是等待的圖案,
然後畫面上也不會顯示【沒有回應】這種鳥鳥的訊息了。
等全部查詢執行完之後,按鈕跟滑鼠就回復原狀,
然後整個查詢時間也正確了!
所以多緒的程式也沒有很難嘛!
如果觀念通了就會覺得很簡單啦!
也許有更簡單的方式吧,不過目前我只知道這樣做囉!
對了!之前有人跟我說 BackgroundWorker 可以直接拿來用,
我之前也有查到,
但是因為查到的資料上面寫說 BackgroundWorker 只能執行一個副執行緒,
而我想要同時多個副執行緒一起執行,
所以 BackgoundWorker 就被我放棄掉啦!
如果妳只有一個副執行緒要處理的話或許 BackgroundWorker 就可以滿足你的要求了吧?
目前我沒有計畫要用 BackgroundWorker 所以妳可能要自己踹踹囉!
聽我同事說 VS2010 好像對 BackgroundWorker 有再加強,
不過我目前手上沒有得試,
改天如果我有踹過我應該也會 po 上來的。
目前這樣應該已經夠用了,
除非我有再發現甚麼須要寫的吧,
不然多緒的程式應該就到此為止了,
接下來就是應用囉!
沒有留言:
張貼留言