C#中Invoke的用法()-解決子線程訪問主線程式控制件、線程安全等問題

来源:http://www.cnblogs.com/janghe/archive/2017/11/21/7875206.html
-Advertisement-
Play Games

引自https://www.cnblogs.com/lsgsanxiao/p/5523282.html invoke和begininvoke 區別 一直對invoke和begininvoke的使用和概念比較混亂,這兩天看了些資料,對這兩個的用法和原理有了些新的認識和理解。 首先說下,invoke和b ...


引自https://www.cnblogs.com/lsgsanxiao/p/5523282.html

invoke和begininvoke 區別

一直對invoke和begininvoke的使用和概念比較混亂,這兩天看了些資料,對這兩個的用法和原理有了些新的認識和理解。

 首先說下,invoke和begininvoke的使用有兩種情況:

  1. control中的invoke、begininvoke。

  2. delegrate中的invoke、begininvoke。  

  這兩種情況是不同的,我們這裡要講的是第1種。下麵我們在來說下.NET中對invoke和begininvoke的官方定義。

  control.invoke(參數delegate)方法:在擁有此控制項的基礎視窗句柄的線程上執行指定的委托。

  control.begininvoke(參數delegate)方法:在創建控制項的基礎句柄所線上程上非同步執行指定委托。

  根據這兩個概念我們大致理解invoke表是同步、begininvoke表示非同步。但是如何來進行同步和非同步呢?我們來做一個測試。

invoke 例子:

private void button1_Click(object sender, EventArgs e)
{
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"AAA");
            invokeThread = new Thread(new ThreadStart(StartMethod));
            invokeThread.Start();
            string a = string.Empty;
            for (int i = 0; i < 3; i++)      //調整迴圈次數,看的會更清楚
            {
                Thread.Sleep(1000);   
                a = a + "B";
            }
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+a);
}

 private void StartMethod()
{
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"CCC");
            button1.Invoke(new invokeDelegate(invokeMethod));  
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"DDD");
}

 private void invokeMethod()
{
            //Thread.Sleep(3000);
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}

結論:我們運行後,看下程式的運行順序,1AAA->3CCC和1BBB->1EEE ->3DDD 。 

解釋:主線程運行1AAA,然後1BBB和子線程3CCC同時執行,然後通過invoke來將invokemethod方法提交給主線程,然後子線 程等待主線程執行,直到主線程將invokemethod方法執行完成(期間必須等待主線程的任務執行完成,才會去執行invoke提交的任務),最後執 行子線程3DDD。

begininvoke 例子:

private void button1_Click(object sender, EventArgs e)
{
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"AAA");
            invokeThread = new Thread(new ThreadStart(StartMethod));
            invokeThread.Start();
            string a = string.Empty;
            for (int i = 0; i < 3; i++)      //調整迴圈次數,看的會更清楚
            {
                Thread.Sleep(1000);   
                a = a + "B";
            }
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+a);
}

 private void StartMethod()
{
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"CCC");
            button1.BeginInvoke(new invokeDelegate(invokeMethod));  
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"DDD");
}

 private void beginInvokeMethod()
        {
            //Thread.Sleep(3000);
            MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEEEEEEEEEEE");
        }

通過這個兩段代碼的測試比較,我們會發現其實invoke和begininvoke所提交的委托方法都是在主線程中執行的,其實根據我invoke 和begininvoke的定義我們要在子線程中來看這個問題,在invoke例子中我們會發現invoke所提交的委托方法執行完成後,才能繼續執行 DDD;在begininvoke例子中我們會發現begininvoke所提交的委托方法後,子線程講繼續執行DDD,不需要等待委托方法的完成。 那麼現在我們在回想下invoke(同步)和begininvoke(非同步)的概念,其實它們所說的意思是相對於子線程而言的,其實對於控制項的調用總是由 主線程來執行的。我們很多人搞不清這個同步和非同步,主要還是因為我們把參照物選錯了。其實有時候光看概念是很容易理解錯誤的。

解決從不是創建控制項的線程訪問它

 

在多線程編程中,我們經常要在工作線程中去更新界面顯示,而在多線程中直接調用界面控制項的方法是錯誤的做法,Invoke 和 BeginInvoke 就是為瞭解決這個問題而出現的,使你在多線程中安全的更新界面顯示。

正確的做法是將工作線程中涉及更新界面的代碼封裝為一個方法,通過 Invoke 或者 BeginInvoke 去調用,兩者的區別就是一個導致工作線程等待,而另外一個則不會。

而所謂的“一面響應操作,一面添加節點”永遠只能是相對的,使 UI 線程的負擔不至於太大而已,因為界面的正確更新始終要通過 UI 線程去做,我們要做的事情是在工作線程中包攬大部分的運算,而將對純粹的界面更新放到 UI 線程中去做,這樣也就達到了減輕 UI 線程負擔的目的了。

舉個簡單例子說明下使用方法,比如你在啟動一個線程,線上程的方法中想更新窗體中的一個TextBox.. 



using System.Threading; 

//啟動一個線程 
Thread thread=new Thread(new ThreadStart(DoWork)); 
thread.Start(); 

//線程方法 
private void DoWork() 

this.TextBox1.Text="我是一個文本框"; 


如果你像上面操作,在VS2005或2008里是會有異常的... 

正確的做法是用Invoke\BeginInvoke

using System.Threading;
namespace test
{
public partial class Form1 : Form
{
public delegate void MyInvoke(string str1,string str2);
public Form1()
{
InitializeComponent();


}
public void DoWork()
{
MyInvoke mi = new MyInvoke(UpdateForm);
this.BeginInvoke(mi, new Object[] {"我是文本框","haha"});
}
public void UpdateForm(string param1,string parm2)
{
this.textBox1.Text = param1+parm2;
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();
}
}
}
註意代理的使用!  

後面再次補充

 在 WinForm開發過程中經常會用到線程,有時候還往往需要線上程中訪問線程外的控制項,比如:設置textbox的Text屬性等等。如果直接設置程式必 定會報出:從不是創建控制項的線程訪問它,這個異常。通常我們可以採用兩種方法來解決。一是通過設置control的屬性。二是通過delegate,而通 過delegate也有兩種方式,一種是常用的方式,另一種就是匿名方式。下麵分別加以說明.

首先,通過設置control的一個屬性值為false.我們可以在Form_Load方法中添加:Control.CheckForIllegalCrossThreadCalls=false;來解決。設置為false表示不對錯誤線程的調用進行捕獲。這樣線上程中對textbox的Text屬性進行設置時就不會再報錯了。
其次,通過delegate的方法來解決。
普通的委托方法例如:

delegate void SafeSetText(string strMsg);
private void SetText(string strMsg)
{
 if(textbox1.InvokeRequired)
 {
            SafeSetText objSet=new SafeSetText(SetText);
            textbox1.Invoke(objSet,new object[]{strMsg})
 }
 else
 {
   textbox1.Text=strMsg;
 }
}

線上程內需要設置textbox的值時調用SetText方法既可。我們還可以採用另一種委托的方式來實現,那就是匿名代理,例如:

delegate void SafeSetText(string strMsg);
private void SetText2(string strMsg)
{
  SafeSetText objSet = delegate(string str)
   {
       textBox1.Text = str;
   }
   textBox1.Invoke(objSet,new object[]{strMsg});
}

這樣同樣可以實現。
個人覺得還是採用代理好些。

在C# 3.0及以後的版本中有了Lamda表達式,像上面這種匿名委托有了更簡潔的寫法。.NET Framework 3.5及以後版本更能用Action封裝方法。例如以下寫法可以看上去非常簡潔:

void ButtonOnClick(object sender,EventArgs e)

{

    this.Invoke(new Action(()=>

    {

        button.Text="關閉";

    }));

}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Linux下軟體的安裝一般由3個步驟組成: 若取消編譯: 若卸載軟體: 本節主要討論configure配置腳本。 如下圖所示,有些軟體就有configure配置腳本: 就可以使用命令./configure –help 輸出詳細的選項列表 常用選項如下: --host 編譯運行後的程式,預設為buil ...
  • 三劍客之awk 第1章 awk簡介 1.1 awk簡介 一種名字怪異的語言。 模式掃描和處理。處理文本流,水流。 awk不僅僅是linux系統中的一個命令,而且是一種編程語言,可以用來處理數據和生成報告。 處理的數據可以是一個或多個文件,可以是來自標準輸入,也可以通過管道獲取標準輸入,awk可以 在 ...
  • [20171120]關於find 軟連接問題.txt--//上個星期為了測試oracle參數filesystemio_options,將資料庫做了一次移動.但是我使用find對軟鏈接目錄查詢時--//遇到一些問題做一個記錄1.建立軟鏈接:$ cp -a /mnt/ramdisk/book /home ...
  • 1. CentOS6及以前 在CentOS6及以前的版本中,free命令輸出是這樣的: 第一行: 系統記憶體主要分為四部分:used(程式已使用記憶體),free(空閑記憶體),buffers(buffer cache),cached(Page cache)。 系統總記憶體total = used + fr ...
  • 紙殼CMS發佈了2.3版本,主要是添加了商城功能,強化產品功能。讓您的網站輕鬆實現電子商務。 有關2.3版本的更多信息,請查看以下鏈接: https://github.com/SeriaWei/ZKEACMS.Core/releases/tag/v2.3 ...
  • 返回總目錄 本小節目錄 Move Method(搬移函數) Move Field(搬移欄位) 1Move Method(搬移函數) 概要 你的程式中,有個函數與其所駐類之外的另一個類進行更多交流:調用後者,或被或者調用。 在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委托 ...
  • 在上一章中,詳細介紹了 ASP.NET Core 中的授權策略,在需要授權時,只需要在對應的Controler或者Action上面打上 特性,並指定要執行的策略名稱即可,但是,授權策略是怎麼執行的呢?懷著一顆好奇的心,忍不住來探索一下它的執行流程。 目錄 1. "MVC中的授權" "Authoriz ...
  • static bool CheckPowerOfTwo(ulong num) { return num > 0 && (num & (num - 1)) == 0; } ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...