備注學(xué)院

          LuLu

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

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

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

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

          一:對Windows窗體控件的非線程安全調(diào)用

          該方式是從輔助線程直接調(diào)用。調(diào)用應(yīng)用程序時,調(diào)試器會引發(fā)一個InvalidOperationException,警告對控件的調(diào)用不是線程安全的。

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

          具體做法如下:

           

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

           

          二:對Windows窗體控件的線程安全調(diào)用

          對窗體控件的線程安全調(diào)用需要用委托的方式。

          主要思路:

          1、查詢控件的 InvokeRequired 屬性。

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

          3、如果 InvokeRequired 返回 false,則直接調(diào)用控件。

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

           

             

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

                  
          // 該線程用于對Windows窗體控件的安全調(diào)用

                  
          private Thread demoThread = null;

           

           

          // 該事件句柄創(chuàng)建一個對窗體控件線程安全調(diào)用的線程

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

              
          this.demoThread.Start();
          }


          // 該方法在Worker線程中執(zhí)行并且發(fā)出一個線程安全的調(diào)用

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


          //如果被調(diào)用的線程和創(chuàng)建的TextBox控件不同,該方法就創(chuàng)建一個SetTextCallback,

          // 并且用Invoke方法異步調(diào)用自己。
          // 如果相同,則直接調(diào)用方法設(shè)置Text的屬性。

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

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

              
          else
              
          {
                  
          this.textBox1.Text = text;
              }

          }

          三、使用 BackgroundWorker 進(jìn)行的線程安全調(diào)用
          在應(yīng)用程序中實現(xiàn)多線程的首選方式是 使用 BackgroundWorker 組件。BackgroundWorker 組件使用事件驅(qū)動模型實現(xiàn)多線程。輔助線程運行 DoWork 事件處理程序,創(chuàng)建控件的線程運行 ProgressChanged 和 RunWorkerCompleted 事件處理程序。注意不要從 DoWork 事件處理程序調(diào)用您的任何控件。

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

          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 閱讀(332) 評論(0)  編輯  收藏 所屬分類: C#
          主站蜘蛛池模板: 松江区| 青阳县| 峨山| 南漳县| 洛川县| 邛崃市| 车险| 石景山区| 金阳县| 咸阳市| 镇巴县| 徐水县| 陈巴尔虎旗| 吕梁市| 奈曼旗| 阿克苏市| 托克逊县| 城口县| 将乐县| 碌曲县| 筠连县| 奉贤区| 南和县| 汝州市| 哈巴河县| 横峰县| 富蕴县| 龙川县| 漾濞| 大连市| 荥经县| 山阳县| 蓬莱市| 通江县| 南江县| 平武县| 兴山县| 巨野县| 华安县| 明水县| 怀来县|