線程間安全調用控件的最優實現

http://blog.csdn.net/coconutyf/article/details/6653325

我們知道,如果試圖訪問控件的方法或屬性之一的線程不是創建該控件的線程,則通常會導致不可預知的結果,因此在調試器外部啟動應用程序時,非法跨線程調用將始終引發異常。

為了避免這種情況的發生,通常大家都會用一些很不經濟的手段來解決,例如我要跨線程為一個名為tb_Name的TextBox控件的Text屬性賦值,一般大家會怎麼做呢?

分2步走,第一定義一個委托,然後定義一個實現該委托的方法,如下所示:

[csharp]  view plain copy
  1. delegate void SetTextboxTextHandler(TextBox txtBox, string text);  
  2. void SetTextboxText(TextBox txtBox, string text)  
  3. {  
  4.     if (txtBox.InvokeRequired)  
  5.     {  
  6.         SetTextboxTextHandler del = new SetTextboxTextHandler(SetTextboxText);  
  7.         txtBox.Invoke(del, txtBox, text);  
  8.     }  
  9.     else  
  10.         txtBox.Text = text;  
  11. }  


如果要獲取這個TextBox的值怎麼辦?

再分兩步走,定義一個有返回值的委托,在定義一個實現該委托的方法,如下所示:

[csharp]  view plain copy
  1. delegate string GetTextboxTextHandler(TextBox txtBox);  
  2. string GetTextboxText(TextBox txtBox)  
  3. {  
  4.     if (txtBox.InvokeRequired)  
  5.     {  
  6.         GetTextboxTextHandler del = new GetTextboxTextHandler(GetTextboxText);  
  7.         return txtBox.Invoke(del, txtBox).ToString();  
  8.     }  
  9.     else  
  10.         return txtBox.Text;  
  11. }  


如果我要設置控件的Enable屬性怎麼辦?依上例再來一遍,是不是要崩潰了?那麼我們有沒有更方便更簡潔的方式來實現呢?當然有。這個方法前期我們需要多准備准備,但後期就太省事了。

第一步,擴展Control類,我們定義一個靜態類 ControlExtensions,如下所示:

[csharp]  view plain copy
  1. using System;  
  2. using System.Linq;  
  3. using System.Collections.Generic;  
  4. using System.Text;  
  5. using System.Windows.Forms;  
  6. using System.Threading;  
  7.   
  8. namespace Com.Yuan.Threading  
  9. {  
  10.     public static class ControlExtensions  
  11.     {  
  12.         /// <summary>  
  13.         /// 無需返回值的情況  
  14.         /// </summary>  
  15.         /// <param name="ctl"></param>  
  16.         /// <param name="action"></param>  
  17.         public static void SafeInvoke(this Control ctl, Action action)  
  18.         {  
  19.             if (ctl.InvokeRequired)  
  20.                 ctl.Invoke(action, null);  
  21.             else  
  22.                 action.Invoke();  
  23.         }  
  24.   
  25.         /// <summary>  
  26.         /// 需要返回值的情況  
  27.         /// </summary>  
  28.         /// <typeparam name="TResult"></typeparam>  
  29.         /// <param name="ctl"></param>  
  30.         /// <param name="func"></param>  
  31.         /// <returns></returns>  
  32.         public static TResult SafeInvoke<TResult>(this Control ctl, Func<TResult> func)  
  33.         {  
  34.             if (ctl.InvokeRequired)  
  35.                 return (TResult)ctl.Invoke(func, null);  
  36.             else  
  37.                 return func.Invoke();  
  38.         }  
  39.     }  
  40. }  


 


然後在需要用到跨線程安全調用控件的時候,添加:

[csharp]  view plain copy
  1. using Com.Yuan.Threading;  


 

[csharp]  view plain copy
  1. void TestSafeInvoke_SetText()  
  2. {  
  3.     //啟動一個新線程,在新線程中更改tb_Name的Text屬性  
  4.     ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>  
  5.     {  
  6.         tb_Name.SafeInvoke(new Action(() => { tb_Name.Text = "老趙"; }));  
  7.     }));  
  8. }  


如何,tb_Name.SafeInvoke(new Action(() => { tb_Name.Text = "老趙"; })); 一句話就完成了

那如果需要訪問控件屬性怎麼辦呢?也很簡單,如下所示:

[csharp]  view plain copy
  1. /// <summary>  
  2. /// 跨線程訪問tb_Name的Text屬性  
  3. /// </summary>  
  4. void TestSafeInvoke_GetText()  
  5. {  
  6.     ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>  
  7.     {  
  8.         var txt = tb_Name.SafeInvoke<string>(new Func<string>(() => { return tb_Name.Text; }));  
  9.         Console.WriteLine(txt);  
  10.     }));  
  11. }  
  12.   
  13. /// <summary>  
  14. /// 跨線程訪問tb_Name的Enabled屬性  
  15. /// </summary>  
  16. void TestSafeInvoke_GetEnable()  
  17. {  
  18.     ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>  
  19.     {  
  20.         var enable = tb_Name.SafeInvoke<bool>(new Func<bool>(() => { return tb_Name.Enabled; }));  
  21.         Console.WriteLine(enable);  
  22.     }));  
  23. }  


你可能感兴趣的:(線程間安全調用控件的最優實現)