C#Invoke和BeginInvoke应用详解

article/2025/9/30 8:31:44

最近,在研究Invoke的使用,但是真的是一头雾水,网上看了很多资料,感觉还是看不懂,因为对于入门级的小白,想像不出Invoke的应用场景,更谈不上如何用了?

1、Invoke到底是什么?

Invoke的本质只是一个方法,方法一定是要通过对象来调用的。

一般来说,Invoke其实用法只有两种情况:

  • Control的Invoke
  • Delegate的Invoke

也就是说,Invoke前面要么是一个控件,要么是一个委托对象

2、什么时候用Invoke

2.1 Control的Invoke

Control的Invoke一般用于解决跨线程访问的问题,比如你想操作一个按钮button,你就要用button.Invoke,你想操作一个文本label,你就要用label.Invoke,但是大家会发现很麻烦,如果我想既操作button,又操作label,能不能写在一起呢?当然可以。
我们知道,主窗体是一个Form,Form自然也是继承Control的,所以Form也有Invoke的方法,可以直接调用Form.Invoke,这就是我们常见的this.Invoke。
这就是为什么有的Invoke前面啥都没有的问题,其实前面是this,只不过省略了。

2.2 Delegate的Invoke

Delegate的Invoke其实就是从线程池中调用委托方法执行,Invoke是同步的方式,会卡住调用它的UI线程。很抽象吧。

3、实验

我们来做个简单的实验。

3.1 新建一个From ,来个Button,我想实现的功能是,点击Button时, Button 变成Disable,并开始显示计算1到8(每隔1秒加1),加到8后,跳出循环,然后把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,但是没有实现计数呀,而是一直就这样停止8秒,就像被卡住了一样。

 8秒后,Button控件直接变成Enable。没有1---8出现,逻辑不对啊,怎么回事?????

原因:直接主线程休眠是达不到效果的,此时桌面还处于假死状态,更新不了text值。代码放在了UI线程执行,阻塞了UI的显示,所以中间的结果你看不到。

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>/// 线程函数/// </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;}}
}

真实现了数字的更新。

 功能是实现了,但没有达到我的目的,主角都没有登场,就谢幕了啊。网上查看了资料,说,这种方法,是不稳定的,特别是主窗口控件比较多的时候,很容易出错,造成画面混乱。为什么呢?因为控件是在主线程中创建的(比如this.Controls.Add(...);),进入控件的事件响应函数时,是在控件所在的线程,并不是主线程。在控件的事件响应函数中改变控件的状态,可能与主线程发生线程冲突。如果主线程正在重绘控件外观,此时在别的线程改变控件外观,就会造成画面混乱。

4、主角出场

4.1 C#的委托机制,一般有下面几种方式。

//第一种
btnAddFunction.Invoke(new EventHandler(delegate{button1.Text = "关闭";}));
//第二种
this.Invoke(new EventHandler(delegate{button1.Text = "关闭";}));
//第三种 网上说自C# 3.0开始就有了
this.Invoke(new Action(() =>{ button1.Text = "关闭";}));

现在应用最多就是第3种了,因为现在版本基本上都是4.0以上了,所以线程函数改为如下的:

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>/// 线程函数/// </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);}//虽然不是循环内,请不要忘记,你的调用依然在辅助线程中,所以,还是需要invoke的。this.Invoke(new Action(() =>{btnAddFunction.Text = "点击开始运行";btnAddFunction.Enabled = true;}));}}
}

4.2 Control的Invoke标准用法

其实,对于Control的Invoke,更标准的用法是先加判断,再调用。

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

InvokeRequired是Control的一个属性,官方解释为:
获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。

简单来说,就是如果通过多线程去操作这个控件,那么这个属性则为True,否则为False。

实例化一个 this invoke的用法

​
using System.Threading;public delegate void MyInvoke(string str);//invoke方法创建委托
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"}); //调用invoke方法           
}public void SetTxt(string str)//委托对应的方法
{txtReceive.Text += str;
}​

4.3 Delegate的Invoke 标准写法

对于Delegate的Invoke,我们一般判断这个方法之前,也是做个判断,判断这个委托对象是否为Null,所以更标准的写法如下:

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

5、 Invoke和BeginInvoke

Control.Invoke 和 Control.BeginInvoke

5.1 测试实例1 利用 控件中的Invoke 和 BeginInvoke 方法

作用1:在线程中执行访问和修改UI内容

作用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)控件的线程中执行Action中的委托// 你可以在此获取UI变量 或者 改变UI变量// 但是 ThreadRun线程会被阻塞 等待 Action 执行完成}));this.BeginInvoke(new Action(() =>{MessageBox.Show("BeginInvoke的方法");// 在该this(Form)控件的线程中执行Action中的委托// 你可以在此获取UI变量 或者 改变UI变量// 但是 ThreadRun线程不会被阻塞继续向下执行}));}}
}

5.2 测试实例2 Action 等delegate 中Invoke和BeginInvoke的作用

作用1.Invoke在当前函数中立即执行,相当于直接调用该Action所注册的所有函数,阻塞当前函数帧

作用2.BeginInvoke在当前函数帧中开辟这个线程去执行Action所注册的函数,不阻塞当前函数帧

作用3.BeginInvoke 中有2额外两个参数 (arg1 回调委托,任意参数)用于beginInvoke完成后,执行该某些动作,

注意:结束完回调的函数 所在线程  为调用beginInvoke 的线程
 

 private void Form1_Load(object sender, EventArgs e){// 在当前函数所在线程中执行,当前函数线程阻塞ShowMsg += (str) => { Debug.WriteLine(str); };ShowMsg.Invoke("This is Invoke test!");// 开辟一个线程执行 BeginInvoke Test,当前函数线程不阻塞ShowMsg.BeginInvoke(" BeginInvoke Test", null, null);// AsyncCallback 回调委托// IAsyncResult 异步执行的结果,可以自我继承,添加自定义参数 用于显示异步执行的状态// index(object类型) 外部传参,存储在IAsyncResult.AsyncState中AsyncCallback asyncCallback = ar => { Debug.WriteLine($"beginInvoke 执行完成 回调{ar.AsyncState}"); };int index = 0;ShowMsg.BeginInvoke(" BeginInvoke Test", asyncCallback, index);}

6 应用 测速下载多个文件时耗用的时间 - 异步编程实现

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]));//最后一个参数是回调状态  ,如果不需要回调函数的话,直接null,这样就不需要调用EndInvoke,单纯的BeginInvoke即可}}public void callback(IAsyncResult result){string filename = "";//if (this.listBox1.InvokeRequired)//{//    filename = this.listBox1.EndInvoke(result).ToString();//    this.listBox1.Items.Add(filename + "复制成功");//    this.listBox2.Items.Add(result.AsyncState.ToString() + "复制成功");//}filename = delobj.EndInvoke(result);//this.listBox1.Items.Add(filename + "复制成功");//this.listBox2.Items.Add(result.AsyncState.ToString() + "复制成功");Console.WriteLine(result.AsyncState.ToString() + "复制成功");}/// <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);}}
}


http://chatgpt.dhexx.cn/article/E2yfmM4j.shtml

相关文章

Java工作流框架:探索流程引擎的实现和应用

目前&#xff0c;市面上有很多基于SpringBootVue前后端分离的Java快速开发框架和工作流开发框架可供选择。以下是一些比较流行的框架&#xff1a; 1. Spring Cloud&#xff1a;Spring Cloud是一套基于Spring Boot的开发工具&#xff0c;用于快速构建分布式系统中的服务。它利用…

Java工作流框架和应用场景

一&#xff1a;Java工作流框架是一种用于设计、执行和管理工作流程的技术。以下是几个常见的Java工作流框架&#xff1a; Activiti&#xff1a;Activiti是一款流行的开源Java工作流引擎&#xff0c;它基于BPMN 2.0标准&#xff0c;支持复杂的工作流程设计和管理。Activiti具有高…

工作流使用

#&#x1f33b; 工作流使用 无需开发代码&#xff0c;即可快速创建工作流、表单&#xff0c;并完成审批、监控等操作。 #功能脑图 #特点 基于 Flowable&#xff08;Activiti&#xff09;生来具有的稳定工作流引擎。使用flowable官方流程设计器&#xff0c;功能强大&#xff…

工作流 开源(java工作流框架jbpm)

工作流(工作流) :“在部分或整个业务流程的计算机应用环境中实现自动化” l简单来说&#xff0c;就是用程序管理工作流程&#xff0c;以表格审核和任务处理为主体&#xff0c;实现办公室自动化 工作流帮助器管理业务流程&#xff0c;业务操作保持不变。 工作流是通知流程&…

java工作流开源框架可以提高工作效率吗?

要想回答这个问题&#xff0c;就需要了解什么是java工作流开源框架&#xff0c;以及java工作流开源框架的主要特点是什么。随着大数据时代的拓展发展&#xff0c;低代码开发平台已经在数字化管理时代中深受欢迎&#xff0c;是做好数据管理和提升企业数字化发展步伐的重要工具。…

Java开源 开源工作流

Willow 点击次数&#xff1a;18942 由Huihoo Power开发详细可到其中文主页查看。 OpenWFE 点击次数&#xff1a;17672 OpenWFE是一个开放源码的Java工作流引擎。它是一个完整的业务处理管理套件&#xff1a;一个引擎&#xff0c;一个工作列表&#xff0c;一个Web界面和一个…

Java实现自定义工作流

这篇文章实现java自定义工作流程&#xff0c;对工作流不太熟悉的可以先看下工作流相关文章&#xff1a; 工作流 相关表结构、实体创建 流程主表&#xff1a;tbl_workflow_requestbase&#xff08;这里以项目工地工作流为例&#xff09; CREATE TABLE tbl_workflow_requestba…

JAVA工作流的优雅实现方式

今天查找线上问题&#xff0c;看到一个让我脑洞大开的工作流实现方式。以前用过责任链模式&#xff0c;也用过模板模式实现类工作流的方式&#xff0c;但是对比这个工具&#xff0c;逊色不少&#xff0c;不卖关子了&#xff0c;就是Apache Commons Chain&#xff0c;它是Comman…

Java开源工作流引擎

http://www.open-open.com/08.htm Willow 由Huihoo Power开发详细可到其中文主页查看。 更多Willow信息 OpenWFE OpenWFE是一个开放源码的Java工作流引擎。它是一个完整的业务处理管理套件&#xff1a;一个引擎&#xff0c;一个工作列表&#xff0c;一个Web界面和一个反应器&…

Java工作流管理系统(activity6.0)

activity6.0工作流系统知识点文章 第一章 activity流程部署&#xff08;自动部署与动态BPMN部署&#xff09; 第二章 activity变量使用 第三章 activity权限控制&#xff08;代办任务查询&#xff09; 第四章 activity审核任务&#xff08;签领、完成任务、跳过节点、新增节点…

JAVA实现一个工作流引擎

介绍 工作流是一种将一系列相关的任务和活动组织起来的技术&#xff0c;以便在企业或组织中自动化或半自动化地管理业务流程。工作流技术可以帮助企业或组织更好地管理和优化业务流程&#xff0c;提高生产效率和质量&#xff0c;降低成本和风险。 JAVA作为一种面向对象编程语…

Java 3个常用工作流引擎

一&#xff1a;Java工作流框架是一种用于设计、执行和管理工作流程的技术。以下是几个常见的Java工作流框架&#xff1a; Activiti&#xff1a;Activiti是一款流行的开源Java工作流引擎&#xff0c;它基于BPMN 2.0标准&#xff0c;支持复杂的工作流程设计和管理。Activiti具有高…

Java 流行的工作流引擎

Java 流行的工作流引擎 JBPM 工作流 JBPM 是一个Java业务流程管理系统&#xff0c;是JBoss中一款开源的工作流引擎&#xff0c;是一个轻量级的&#xff0c;使用BPMN 2规范可扩展的 工作流引擎&#xff0c;也是一个工作流管理系统&#xff0c;它可以运行在任何java环境&#x…

Java工作流有哪些?如何快速掌握Java技术

工作流是什么?工作流是指两个或两个以上的人&#xff0c;为了共同的目标&#xff0c;连续的以串行或并行的方式去完成某一业务。Java工作流就是一个基于Java开发的流程框架&#xff0c;是每一个参加郑州Java软件开发培训的学员需要掌握的入门知识。那么常见的Java工作流有哪些…

JAVA工作流

一、 什么是工作流 以请假为例&#xff0c;现在大多数公司的请假流程是这样的 员工打电话&#xff08;或网聊&#xff09;向上级提出请假申请——上级口头同意——上级将请假记录下来——月底将请假记录上交公司——公司将请假录入电脑 采用工作流技术的公司的请假流程是这样…

java工作流详解

什么是工作流&#xff1f; 工作流&#xff1a;两个或两个以上的人&#xff0c;为了共同的目标&#xff0c;连续的以串行或并行的方式去完成某一业务。 业务&#xff1a;工作流所指业务涵盖了与经营相关的活动。 串行或并行&#xff1a;业务中的步骤也许以一步接着一步的方式…

Java开源工作流框架对比

什么是工作流&#xff1f; 工作流&#xff0c;是指“业务​过程的部分或整体在​计算机应用环境下的自动化”。是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。 在计算机中&#xff0c;工作流属于计算机支持的协同工作&#xff08;CSCW&#xff09;的一部分。 工作流…

极简 Java 工作流概念入门

1. 为什么需要工作流 假设我有一个请假需求&#xff0c;流程如下&#xff1a; 请假可以提交给我的上司&#xff0c;上司可以选择批准或者拒绝&#xff0c;无论批准还是拒绝&#xff0c;都会给我一个通知。 这个流程比较简单&#xff0c;我们很容易想到解决方案&#xff0c;不…

Java工作流详解(附6大工作流框架对比)

目录 1.什么是工作流 2.工作流应用场景 3.工作流实现方式 4.有哪些工作流框架? 5.1.Activiti6. 2.Flowable7. 3.Camunda8.4.jBPM9. 5.osworkflow&#xff0c;6.jflow. 10.工作流框架对比 什么是工作流工作流(Worklow) 工作流是对工作流程及其各操作步骤之间业务规则的…

smalldatetime类型

1、MS Sql Server 的smalldatetime 与datetime类型的区别 原来数据库字段为smalldatetime类型&#xff0c; update table set column 11:59:30 发现commit后db中的值为12:00:00 update table set column 11:59:29 发现commit后db中的值为11:59:00 原来smalldatetime是不存储秒…