欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#中Invoke和BeginInvoke實際應(yīng)用詳解

 更新時間:2023年12月13日 14:38:44   作者:微雨夏涼  
這篇文章主要給大家介紹了關(guān)于C#中Invoke和BeginInvoke實際應(yīng)用的相關(guān)資料,Invoke是對象方法,BeginInvoke是靜態(tài)方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

前言

最近,在研究Invoke的使用,但是真的是一頭霧水,網(wǎng)上看了很多資料,感覺還是看不懂,因為對于入門級的小白,想像不出Invoke的應(yīng)用場景,更談不上如何用了?

1、Invoke到底是什么?

Invoke的本質(zhì)只是一個方法,方法一定是要通過對象來調(diào)用的。

一般來說,Invoke其實用法只有兩種情況:

  • Control的Invoke
  • Delegate的Invoke

也就是說,Invoke前面要么是一個控件,要么是一個委托對象

2、什么時候用Invoke

2.1 Control的Invoke

Control的Invoke一般用于解決跨線程訪問的問題,比如你想操作一個按鈕button,你就要用button.Invoke,你想操作一個文本label,你就要用label.Invoke,但是大家會發(fā)現(xiàn)很麻煩,如果我想既操作button,又操作label,能不能寫在一起呢?當(dāng)然可以。
我們知道,主窗體是一個Form,F(xiàn)orm自然也是繼承Control的,所以Form也有Invoke的方法,可以直接調(diào)用Form.Invoke,這就是我們常見的this.Invoke。這就是為什么有的Invoke前面啥都沒有的問題,其實前面是this,只不過省略了。

2.2 Delegate的Invoke

Delegate的Invoke其實就是從線程池中調(diào)用委托方法執(zhí)行,Invoke是同步的方式,會卡住調(diào)用它的UI線程。很抽象吧。

3、實驗

我們來做個簡單的實驗。

3.1 新建一個From ,來個Button,我想實現(xiàn)的功能是,點擊Button時, Button 變成Disable,并開始顯示計算1到8(每隔1秒加1),加到8后,跳出循環(huán),然后把Button Enable,很簡單吧。

小事情拉,于是開始行動,很快就搞定了,代碼如下:

namespace InvokeTest1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// Butten 點擊事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddFunction_Click_1(object sender, EventArgs e)
        {
            btnAddFunction.Enabled = false;
            for (int i = 1; i < 8; i++)
            {
                btnAddFunction.Text = i.ToString();
                Thread.Sleep(1000);
            }
            btnAddFunction.Text = "點擊開始運行";
            btnAddFunction.Enabled = true;
        }
    }
}

開始運行,點擊Button,控件是變成Disable,但是沒有實現(xiàn)計數(shù)呀,而是一直就這樣停止8秒,就像被卡住了一樣。

 8秒后,Button控件直接變成Enable。沒有1---8出現(xiàn),邏輯不對啊,怎么回事?????

原因:直接主線程休眠是達不到效果的,此時桌面還處于假死狀態(tài),更新不了text值。代碼放在了UI線程執(zhí)行,阻塞了UI的顯示,所以中間的結(jié)果你看不到。

3.2 找到了原因,那就好辦了,既然代碼放在了UI線程,那就新建個線程,在那里面更新UI控件好了。信心滿滿的開始行動。

namespace InvokeTest1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Butten 點擊事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddFunction_Click_1(object sender, EventArgs e)
        {
            //啟動一個線程,在這個線程里更新Button值
            new Thread(ThreadTask).Start();
        }

        /// <summary>
        /// 線程函數(shù)
        /// </summary>
        public void ThreadTask()
        {
            btnAddFunction.Enabled = false;
            for (int i = 1; i < 8; i++)
            {
                btnAddFunction.Text = i.ToString();
                Thread.Sleep(1000);
            }
            btnAddFunction.Text = "點擊開始運行";
            btnAddFunction.Enabled = true;
        }

    }
}

真實現(xiàn)了數(shù)字的更新。

 功能是實現(xiàn)了,但沒有達到我的目的,主角都沒有登場,就謝幕了啊。網(wǎng)上查看了資料,說,這種方法,是不穩(wěn)定的,特別是主窗口控件比較多的時候,很容易出錯,造成畫面混亂。為什么呢?因為控件是在主線程中創(chuàng)建的(比如this.Controls.Add(...);),進入控件的事件響應(yīng)函數(shù)時,是在控件所在的線程,并不是主線程。在控件的事件響應(yīng)函數(shù)中改變控件的狀態(tài),可能與主線程發(fā)生線程沖突。如果主線程正在重繪控件外觀,此時在別的線程改變控件外觀,就會造成畫面混亂。

4、主角出場

4.1 C#的委托機制,一般有下面幾種方式。

//第一種
btnAddFunction.Invoke(new EventHandler(delegate{button1.Text = "關(guān)閉";}));
//第二種
this.Invoke(new EventHandler(delegate{button1.Text = "關(guān)閉";}));
//第三種 網(wǎng)上說自C# 3.0開始就有了
this.Invoke(new Action(() =>{ button1.Text = "關(guān)閉";}));

現(xiàn)在應(yīng)用最多就是第3種了,因為現(xiàn)在版本基本上都是4.0以上了,所以線程函數(shù)改為如下的:

namespace InvokeTest1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Butten 點擊事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddFunction_Click_1(object sender, EventArgs e)
        {
            //啟動一個線程,在這個線程里更新Button值
            new Thread(ThreadTask).Start();
        }

        /// <summary>
        /// 線程函數(shù)
        /// </summary>
        public void ThreadTask()
        {
            //首先將button對象禁用
            this.Invoke(new Action(() =>
            {
                btnAddFunction.Enabled = false;
            }));
            for (int i = 0; i < 10; i++)
            {
                this.Invoke(new Action(() =>
                {
                    btnAddFunction.Text = i.ToString();
                }));
                Thread.Sleep(1000);
            }
            //雖然不是循環(huán)內(nèi),請不要忘記,你的調(diào)用依然在輔助線程中,所以,還是需要invoke的。
            this.Invoke(new Action(() =>
            {
                btnAddFunction.Text = "點擊開始運行";
                btnAddFunction.Enabled = true;
            }));

        }

    }
}

4.2 Control的Invoke標(biāo)準(zhǔn)用法

其實,對于Control的Invoke,更標(biāo)準(zhǔn)的用法是先加判斷,再調(diào)用。

if (this.lbl_Value.InvokeRequired)
{
    this.lbl_Value.Invoke(new Action(() =>
    {
        this.lbl_Value.Text = "Invoke功能測試";
    }));
}
else
{
    this.lbl_Value.Text = "Invoke測試失效";
}

InvokeRequired是Control的一個屬性,官方解釋為:獲取一個值,該值指示調(diào)用方在對控件進行方法調(diào)用時是否必須調(diào)用 Invoke 方法,因為調(diào)用方位于創(chuàng)建控件所在的線程以外的線程中。如果控件的 Handle 是在與調(diào)用線程不同的線程上創(chuàng)建的(說明您必須通過 Invoke 方法對控件進行調(diào)用),則為 true;否則為 false。

簡單來說,就是如果通過多線程去操作這個控件,那么這個屬性則為True,否則為False。

實例化一個 this invoke的用法

?
using System.Threading;
public delegate void MyInvoke(string str);//invoke方法創(chuàng)建委托
private void btnStartThread_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(new ThreadStart(DoWord));
    thread.Start();
}
public void DoWord()
{            
     MyInvoke mi = new MyInvoke(SetTxt);//實例化一個委托,并且指定委托方法
     BeginInvoke(mi,new object[]{"abc"}); //調(diào)用invoke方法           
}
public void SetTxt(string str)//委托對應(yīng)的方法
{
     txtReceive.Text += str;
}
?

4.3 Delegate的Invoke 標(biāo)準(zhǔn)寫法

對于Delegate的Invoke,我們一般判斷這個方法之前,也是做個判斷,判斷這個委托對象是否為Null,所以更標(biāo)準(zhǔn)的寫法如下:

DelegateInvokeFun testDelegate = new DelegateInvokeFun(DelegateInvokeMethod);
testDelegate?.Invoke();

5、 Invoke和BeginInvoke

Control.Invoke 和 Control.BeginInvoke

5.1 測試實例1 利用 控件中的Invoke 和 BeginInvoke 方法

作用1:在線程中執(zhí)行訪問和修改UI內(nèi)容

作用2:Invoke可以阻塞線程,等待UI操作返回

作用3:BeginInvoke不阻塞線程,后臺刷新UI,提高程序的流暢性
 

public partial class frmMain : Form
{
      public frmMain ()
      {
          InitializeComponent();
      }
 
      public void ThreadRun()
      {
          while (true)
          {
             Thread.Sleep(1);
 
                this.Invoke(new Action(() => 
                { 
                    MessageBox.Show("Invoke的方法");
                    // 在該this(Form)控件的線程中執(zhí)行Action中的委托
                    // 你可以在此獲取UI變量 或者 改變UI變量
                    // 但是 ThreadRun線程會被阻塞 等待 Action 執(zhí)行完成
                }));
 
                this.BeginInvoke(new Action(() =>
                {
                    MessageBox.Show("BeginInvoke的方法");
                    // 在該this(Form)控件的線程中執(zhí)行Action中的委托
                    // 你可以在此獲取UI變量 或者 改變UI變量
                    // 但是 ThreadRun線程不會被阻塞繼續(xù)向下執(zhí)行
                }));
          }
      }
}

5.2 測試實例2 Action 等delegate 中Invoke和BeginInvoke的作用

作用1.Invoke在當(dāng)前函數(shù)中立即執(zhí)行,相當(dāng)于直接調(diào)用該Action所注冊的所有函數(shù),阻塞當(dāng)前函數(shù)幀

作用2.BeginInvoke在當(dāng)前函數(shù)幀中開辟這個線程去執(zhí)行Action所注冊的函數(shù),不阻塞當(dāng)前函數(shù)幀

作用3.BeginInvoke 中有2額外兩個參數(shù) (arg1 回調(diào)委托,任意參數(shù))用于beginInvoke完成后,執(zhí)行該某些動作,

注意:結(jié)束完回調(diào)的函數(shù) 所在線程  為調(diào)用beginInvoke 的線程
 

 private void Form1_Load(object sender, EventArgs e)
        {
            // 在當(dāng)前函數(shù)所在線程中執(zhí)行,當(dāng)前函數(shù)線程阻塞
            ShowMsg += (str) => { Debug.WriteLine(str); };
            ShowMsg.Invoke("This is Invoke test!");

            // 開辟一個線程執(zhí)行 BeginInvoke Test,當(dāng)前函數(shù)線程不阻塞
            ShowMsg.BeginInvoke(" BeginInvoke Test", null, null);


            // AsyncCallback 回調(diào)委托
            // IAsyncResult 異步執(zhí)行的結(jié)果,可以自我繼承,添加自定義參數(shù) 用于顯示異步執(zhí)行的狀態(tài)
            // index(object類型) 外部傳參,存儲在IAsyncResult.AsyncState中
            AsyncCallback asyncCallback = ar => { Debug.WriteLine($"beginInvoke 執(zhí)行完成 回調(diào){ar.AsyncState}"); };
            int index = 0;
            ShowMsg.BeginInvoke(" BeginInvoke Test", asyncCallback, index);
        }

6 應(yīng)用 測速下載多個文件時耗用的時間 - 異步編程實現(xiàn)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace InvokeDemo
{
    public partial class 異步編程 : Form
    {
        public delegate string delegateObj(string paht);
        public 異步編程()
        {
            InitializeComponent();
            delobj = new delegateObj(copyFile);
        }
        delegateObj delobj=null;
        /// <summary>
        /// 同步拷貝文件,并輸出文件名稱
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            String[] paths = Directory.GetFiles(@"F:\迅雷下載");
            for (int i = 0; i < paths.Length; i++)
            {
                this.listBox1.Items.Add(copyFile(paths[i]));
            }
        }
        /// <summary>異步拷貝文件,并輸出文件名
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            
            String[] paths = Directory.GetFiles(@"F:\迅雷下載");
            for (int i = 0; i < paths.Length; i++)
            {
                delobj.BeginInvoke(paths[i], callback, Path.GetFileName(paths[i]));//最后一個參數(shù)是回調(diào)狀態(tài)  ,如果不需要回調(diào)函數(shù)的話,直接null,這樣就不需要調(diào)用EndInvoke,單純的BeginInvoke即可
            }
        }
        public void callback(IAsyncResult result)
        {
            string filename = "";
            //if (this.listBox1.InvokeRequired)
            //{
            //    filename = this.listBox1.EndInvoke(result).ToString();
            //    this.listBox1.Items.Add(filename + "復(fù)制成功");
            //    this.listBox2.Items.Add(result.AsyncState.ToString() + "復(fù)制成功");
                
            //}
            filename = delobj.EndInvoke(result);
            //this.listBox1.Items.Add(filename + "復(fù)制成功");
            //this.listBox2.Items.Add(result.AsyncState.ToString() + "復(fù)制成功");
 
            Console.WriteLine(result.AsyncState.ToString() + "復(fù)制成功");
        }
        /// <summary>
        /// 拷貝文件用
        /// </summary>
        /// <param name="filename"></param>
        public string copyFile(string filename)
        {
            if (filename.EndsWith("desktop.ini"))
                return "desktop.ini";
            if (File.Exists(@"F:\迅雷下載2\" + Path.GetFileName(filename)))
            {
                File.Delete(@"F:\迅雷下載2\" + Path.GetFileName(filename));
            }
            File.Copy(filename, @"F:\迅雷下載2\" + Path.GetFileName(filename));
            return Path.GetFileName(filename);
        }
    }
}

總結(jié) 

到此這篇關(guān)于C#中Invoke和BeginInvoke實際應(yīng)用詳解的文章就介紹到這了,更多相關(guān)C# Invoke和BeginInvoke應(yīng)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c# 單例模式的實現(xiàn)方法

    c# 單例模式的實現(xiàn)方法

    這篇文章主要介紹了c# 單例模式的實現(xiàn)方法,文中講解非常細致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-08-08
  • C# Winform實現(xiàn)截圖工具的示例代碼

    C# Winform實現(xiàn)截圖工具的示例代碼

    這篇文章主要為大家詳細介紹了如何使用C# Winform制作一個簡單的截圖工具,從而實現(xiàn)截圖功能,文中的示例代碼講解詳細,有需要的可以參考下
    2024-02-02
  • C#操作 JSON方法匯總

    C#操作 JSON方法匯總

    本文給大家匯總了一下使用C#操作Json的方法,非常的簡單實用,有需要的小伙伴可以參考下
    2015-10-10
  • WPF實現(xiàn)授權(quán)碼顯示密文并支持換行

    WPF實現(xiàn)授權(quán)碼顯示密文并支持換行

    這篇文章主要為大家詳細介紹了如何使用WPF實現(xiàn)授權(quán)碼顯示密文并支持換行,文中的示例代碼講解詳細,有需要的小伙伴可以參考一下
    2024-10-10
  • c#中合并DataTable重復(fù)行的值

    c#中合并DataTable重復(fù)行的值

    c#中合并DataTable重復(fù)行的值,需要的朋友可以參考一下
    2013-05-05
  • C#表達式中的動態(tài)查詢詳解【譯】

    C#表達式中的動態(tài)查詢詳解【譯】

    這篇文章主要給大家介紹了關(guān)于C#表達式中動態(tài)查詢的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • C# Winform 實現(xiàn)TCP發(fā)消息

    C# Winform 實現(xiàn)TCP發(fā)消息

    這篇文章主要介紹了C# Winform 實現(xiàn)TCP發(fā)消息的示例,幫助大家更好的理解和學(xué)習(xí)使用c#技術(shù),感興趣的朋友可以了解下
    2021-03-03
  • 基于C#實現(xiàn)在圖片上繪制文字

    基于C#實現(xiàn)在圖片上繪制文字

    這篇文章主要為大家詳細介紹了如何利用C#實現(xiàn)在圖片上繪制文字的效果,文中的示例代碼講解詳細,對我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-12-12
  • C#遞歸題目實例代碼

    C#遞歸題目實例代碼

    這篇文章主要介紹了C#遞歸題目實例代碼,有需要的朋友可以參考一下
    2014-01-01
  • c#中文轉(zhuǎn)unicode字符示例分享

    c#中文轉(zhuǎn)unicode字符示例分享

    本文介紹了中文轉(zhuǎn)unicode字符的方法,還有UNICODE字符轉(zhuǎn)為中文的方法,大家參考使用吧
    2014-01-01

最新評論