備注學院

          LuLu

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            5 隨筆 :: 50 文章 :: 16 評論 :: 0 Trackbacks

          訪問 Windows 窗體控件本質上不是線程安全的。如果有兩個或多個線程操作某一控件的狀態,則可能會迫使該控件進入一種不一致的狀態。還可能出現其他與線程相關的 bug,包括爭用情況和死鎖。確保以線程安全方式訪問控件非常重要。

          .NET Framework 有助于在以非線程安全方式訪問控件時檢測到這一問題。在調試器中運行應用程序時,如果創建某控件的線程之外的其他線程試圖調用該控件,則調試器會引發一個 InvalidOperationException,并提示消息:“從不是創建控件 control name 的線程訪問它。”

          有三種方法可以從線程訪問Win窗體的控件:非線程安全方式;線程安全調用;使用BackgroundWorker進行的線程安全調用。其中,只有線程的安全調用可以宏觀并行處理。(另外兩種方式都是在線程運行時接受命令,但在線程執行完以后才執行)。

          一:對Windows窗體控件的非線程安全調用

          該方式是從輔助線程直接調用。調用應用程序時,調試器會引發一個InvalidOperationException,警告對控件的調用不是線程安全的。

          可以通過將 CheckForIllegalCrossThreadCalls 屬性的值設置為 false 來禁用此異常。這會使控件以與在 Visual Studio 2003 下相同的方式運行。

          具體做法如下:

           

          public Form1()
                  
          {
                      InitializeComponent();
                      Control.CheckForIllegalCrossThreadCalls 
          = false;//這一行是關鍵 用于對線程的不安全調用
                  }

           

          二:對Windows窗體控件的線程安全調用

          對窗體控件的線程安全調用需要用委托的方式。

          主要思路:

          1、查詢控件的 InvokeRequired 屬性。

          2、如果 InvokeRequired 返回 true,則使用實際調用控件的委托來調用 Invoke。

          3、如果 InvokeRequired 返回 false,則直接調用控件。

          例子:在TextBox控件中輸出相應的信息,SetText為textbox的內容設置方法,SetTextDelegate的委托類型封裝 SetText方法。TextBox控件的InvokeRequired返回true是,SetText方法創建SetTextDelegate的一個實 例,并調用窗體的Invoke方法。是的SetText方法被創建TextBox控件的線程調用。

           

             

            / /該委托使在一個TextBox控件上設置text屬性的異步調用功能為真
                  
          delegate void SetTextCallback(string text);

                  
          // 該線程用于對Windows窗體控件的安全調用

                  
          private Thread demoThread = null;

           

           

          // 該事件句柄創建一個對窗體控件線程安全調用的線程

          private void setTextSafeBtn_Click( object sender,  EventArgs e)
          {
              
          this.demoThread =
                  
          new Thread(new ThreadStart(this.ThreadProcSafe));

              
          this.demoThread.Start();
          }


          // 該方法在Worker線程中執行并且發出一個線程安全的調用

          private void ThreadProcSafe()
          {
              
          this.SetText("This text was set safely.");
          }


          //如果被調用的線程和創建的TextBox控件不同,該方法就創建一個SetTextCallback,

          // 并且用Invoke方法異步調用自己。
          // 如果相同,則直接調用方法設置Text的屬性。

          private void SetText(string text)
          {
              
          // 獲取的InvokeRequired將調用的線程ID和創建的線程ID向比較。 
              
          //如果兩個線程ID不同,則返回true 

                
          if (this.textBox1.InvokeRequired)
               
          {   
                  SetTextCallback d 
          = new SetTextCallback(SetText);
                  
          this.Invoke(d, new object[] { text });
              }

              
          else
              
          {
                  
          this.textBox1.Text = text;
              }

          }

          三、使用 BackgroundWorker 進行的線程安全調用
          在應用程序中實現多線程的首選方式是 使用 BackgroundWorker 組件。BackgroundWorker 組件使用事件驅動模型實現多線程。輔助線程運行 DoWork 事件處理程序,創建控件的線程運行 ProgressChanged 和 RunWorkerCompleted 事件處理程序。注意不要從 DoWork 事件處理程序調用您的任何控件。

          下面的代碼示例不異步執行任何工作,因此沒有 DoWork 事件處理程序的實現。TextBox 控件的 Text 屬性在 RunWorkerCompleted 事件處理程序中直接設置。

          private void setTextBackgroundWorkerBtn_Click(object sender,  EventArgs e)
          {
              
          this.backgroundWorker1.RunWorkerAsync();
          }


          private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
          {
              
          this.textBox1.Text =
                  
          "This text was set safely by BackgroundWorker.";
          }




          posted on 2008-10-31 10:32 smildlzj 閱讀(329) 評論(0)  編輯  收藏 所屬分類: C#
          主站蜘蛛池模板: 泰州市| 射洪县| 乐业县| 江源县| 南华县| 奇台县| 永春县| 石景山区| 江安县| 黄石市| 通渭县| 文安县| 新竹市| 竹山县| 罗平县| 玉环县| 海丰县| 大城县| 九龙坡区| 鸡泽县| 万州区| 洛浦县| 格尔木市| 东阳市| 隆子县| 河津市| 铁岭市| 开远市| 彰化市| 闵行区| 禹城市| 壤塘县| 彝良县| 南投市| 离岛区| 康保县| 新竹市| 泸西县| 九江市| 额济纳旗| 松江区|