C#中Delegate/Control的Invoke/BeginInvoke/EndInvoke

article/2025/9/30 7:34:17

目录

一、前言

二、背景

三、Delegate的Invoke/BeginInvoke/EndInvoke

1、基于[需求1]

1.1、直接在主线程中运行“耗时操作”

1.2、通过Thread将“耗时操作”放在子线程中运行

1.3、通过Delegate.BeginInvoke()将“耗时操作”放在子线程中运行

1.4、总结

2、基于[需求2]

2.1、一去不复返

2.2、去了还要回->轮询

2.3、去了还要回->WaitOne

2.4、去了还要回->回调

3、Delegate.Invoke()

四、Control的Invoke/BeginInvoke/EndInvoke

1、基于[需求3]

1.1、禁用“检查是否跨线程访问控件”,这是不安全的做法

1.2、Control.Invoke(Delegate)

1.3、Control.BeginInvoke(Delegate)

1.4、总结

五、参考


一、前言

        此文章是我阅读了许多前人的文章、博客后,根据自己的理解做的总结、记录。有不正确的地方望各位多加指正,有侵权的地方望各位提醒我添加引用备注出处。

        此文章迭代更新中(20221201-1619)。

        对C#中的Delegate、Event、Thread,WinForm中的Control有基本了解。

二、背景

        当运行一个WinForm窗体时,UI线程(主线程)上运行着一个消息循环,可以理解“消息循环”是一个死循环,每次循环都会从“消息队列”中取出一个“消息”(WinForm窗体中的用户操作,比如鼠标单击/双击/移入/移出操作,键盘输入等),然后处理这个“消息”,体现在WinForm窗体上就是实时地响应了用户的操作。

        如果有个[需求1]:要在点击WinForm窗体中的一个Button后,执行一个非常耗时的操作(比如,sleep 10s)。如果直接在主线程中执行这个耗时操作,主线程因为不能进行消息循环导致WinForm窗体看起来卡死,无法响应用户操作。所以,此时应该创建一个子线程,然后将耗时操作放在子线程中执行,此时,会用到Thread,或者Delegate.BeginInvoke()来创建子线程。

        如果有个[需求2]:在[需求1]的基础上,“耗时操作”在子线程中执行完后,需要获取“耗时操作”的执行结果。

        如果有个[需求3]:在[需求1]、[需求2]的基础上,“耗时操作”在子线程中执行完后,需要刷新WinForm中的某个Control(比如,需要刷新TextBox文本框中的值。此时也可以理解为是[需求2]的一种特殊情况)。如果“刷新操作”也放在子线程中执行,会抛出运行时异常(跨线程访问控件,即控件是在主线程中创建的,但是却在子线程中对控件进行了刷新)。所以,此时应该将“刷新操作”整体封装成一个Delegate,然后通过Control.Invoke(Delegate)、Control.BeginInvoke(Delegate)将“刷新操作”交给主线程执行,就能解决“跨线程访问控件”的运行时异常。

三、Delegate对象的Invoke/BeginInvoke/EndInvoke

1、基于[需求1]

1.1、直接在主线程中运行“耗时操作”

public Form1()
{InitializeComponent();Debug.Listeners.Add(new ConsoleTraceListener());
}private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(10000);Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}protected override void WndProc(ref Message m)
{Debug.WriteLine(m.Msg.ToString());base.WndProc(ref m);return;
}

        点击Button后,主线程Sleep 10s。此时WinForm看起来卡死,不响应用户在UI中的操作;Console输出的用户在UI中的操作信息看起来也卡住。

        当主线程Sleep结束后,WinForm才响应用户在UI中的操作;Console才继续输出主线程Sleep期间用户在UI中的操作信息。

1.2、通过Thread将“耗时操作”放在子线程中运行

private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread thread = new Thread(new ThreadStart(SubThreadMethod));thread.IsBackground = true;thread.Start();Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}private void SubThreadMethod()
{Console.WriteLine(string.Format("Begin SubThread ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(10000);Console.WriteLine(string.Format("End SubThread ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}

        点击Button后,通过new Thread()创建一个子线程,子线程执行Thread封装的SubThreadMethod,即Sleep 10s;主线程上仍然运行着消息循环。此时WinForm仍然可以响应用户在UI中的操作。

1.3、通过Delegate对象.BeginInvoke()将“耗时操作”放在子线程中运行

private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));CalcDel myCalcDel = new CalcDel(ExeMethod);Console.WriteLine(string.Format("Before BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));myCalcDel.BeginInvoke(null, null);Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}public delegate void CalcDel();public void ExeMethod()
{Console.WriteLine(string.Format("Start ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(10000);Console.WriteLine(string.Format("End ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}

        点击Button后,通过Delegate对象.BeginInvoke()创建一个子线程,子线程执行Delegate对象封装的ExeMethod,即Sleep 10s;主线程上仍然运行着消息循环。效果看起来等同于[1.2]

1.4、总结

        对比2、3发现,Delegate对象.BeginInvoke()实质上是Thread,创建了一个子线程,然后在子线程中运行Delegate对象封装的操作。

        了解了Delegate对象.BeginInvoke()后,再详细对比下Delegate对象.BeginInvoke()、Delegate对象.EndInvoke()的若干用法。

2、基于[需求2]

此段内容,参考文章[WinForm二三事(二)异步操作 - 横刀天笑 - 博客园],并进行完善

2.1、一去不复返

        1.3中展示的就是,子线程运行的“耗时操作”结束后,主线程不需要获取任何关于子线程结束的信息。

2.2、去了还要回->轮询

private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Func<int> myAction = new Func<int>(ExeMethod);Console.WriteLine(string.Format("Begin BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));IAsyncResult asynResult = myAction.BeginInvoke(null, null);Console.WriteLine(string.Format("End BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));DateTime lastTime = DateTime.MinValue;while (!asynResult.IsCompleted){DateTime curTime = DateTime.Now;if((curTime - lastTime).TotalSeconds > 2){Console.WriteLine(string.Format("NotComplete ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));lastTime = curTime;}}Console.WriteLine(string.Format("Complete ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));int result = myAction.EndInvoke(asynResult);Console.WriteLine(string.Format("End Click result:{0} ThreadId:{1} {2}", result, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}private int ExeMethod()
{Console.WriteLine(string.Format("Begin ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(10 * 1000);Console.WriteLine(string.Format("End ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return 25;
}

        点击Button后,通过Delegate对象.BeginInvoke()创建一个子线程,子线程执行Delegate对象封装的ExeMethod,即Sleep 10s;主线程上运行着循环,监视子线程是否执行完毕。

        如果子线程没有执行完毕,asynResult.IsCompleted始终为false,主线程中的循环一直进行;如果子线程执行完毕,asynResult.IsCompleted变成true,主线程跳出循环。

        主线程在此while循环期间,因为主线程没有运行消息循环,所以WinForm看起来卡死,不响应用户在UI中的操作。

        主线程、子线程分别在哪些时间段执行,是由调度算法分配的。比如,截图中,Delegate对象.BeginInvoke()语句执行后,并没有立即执行由Delegate对象.BeginInvoke()语句创建的子线程,而是由调度算法决定什么时候执行子线程中的语句:

        1、先拥有控制权的主线程执行了一阵子while循环,然后把控制权交给子线程执行ExeMethod;

        2、当子线程Sleep后,又将控制权交给主线程执行while循环;

        3、主线程执行了一阵子while循环,等子线程Sleep结束后,将控制权交给子线程;

        4、子线程执行完毕后,IAsyncResult对象.IsCompleted由false变为true,控制权交给主线程;

        5、主线程跳出while循环。

IAsyncResult asynResult = myAction.BeginInvoke(null, null);while(!asynResult.IsCompleted){}

        会使执行该语句的当前线程一直循环,直到子线程运行完Delegate对象封装的操作。

2.3、去了还要回->WaitOne

private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Func<int> myAction = new Func<int>(ExeMethod);Console.WriteLine(string.Format("Begin BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));IAsyncResult asynResult = myAction.BeginInvoke(null, null);Console.WriteLine(string.Format("End BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));if (asynResult.AsyncWaitHandle.WaitOne(5 * 1000, true)){Console.WriteLine(string.Format("Begin wait ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));int result = myAction.EndInvoke(asynResult);Console.WriteLine(string.Format("End wait result:{0} ThreadId:{1} {2}", result, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));}Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}private int ExeMethod()
{Console.WriteLine(string.Format("Begin ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(10 * 1000);Console.WriteLine(string.Format("End ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return 25;
}

         1、点击Button后,主线程执行到IAsyncResult asynResult = myAction.BeginInvoke(null, null)时,会创建一个子线程跑ExeMethod,不过此时主线程还拥有控制权,所以主线程继续执行到asynResult.AsyncWaitHandle.WaitOne(5 * 1000, true),接着主线程会阻塞等待5s(因为子线程还没执行完毕,所以WaitOne让主线程阻塞等待),此时主线程的控制权被调度算法收回;

        [主线程在此阻塞等待期间,因为主线程没有运行消息循环,所以WinForm看起来卡死,不响应用户在UI中的操作]

        2、调度算法将控制权交给子线程,子线程Sleep 10s,此时子线程的控制权被调度算法收回;

        3、5s后,主线程结束了阻塞等待,调度算法将控制权交给主线程,主线程跳过if(asynResult.AsyncWaitHandle.WaitOne(5 * 1000, true)){}花括号中的语句(因为子线程还没执行完毕,所以if条件不满足),直接执行if(){}之后的语句,然后主线程结束,主线程的控制权被调度算法收回;

        4、又过了5s后,子线程结束了Sleep,调度算法将控制权交给子线程,然后子线程结束,子线程的控制权被调度算法收回。

private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Func<int> myAction = new Func<int>(ExeMethod);Console.WriteLine(string.Format("Begin BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));IAsyncResult asynResult = myAction.BeginInvoke(null, null);Console.WriteLine(string.Format("End BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));if (asynResult.AsyncWaitHandle.WaitOne(10 * 1000, true)){Console.WriteLine(string.Format("Begin wait ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));int result = myAction.EndInvoke(asynResult);Console.WriteLine(string.Format("End wait result:{0} ThreadId:{1} {2}", result, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));}Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}private int ExeMethod()
{Console.WriteLine(string.Format("Begin ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(5 * 1000);Console.WriteLine(string.Format("End ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return 25;
}

        1、点击Button后,主线程执行到IAsyncResult asynResult = myAction.BeginInvoke(null, null)时,会创建一个子线程跑ExeMethod,不过此时主线程还拥有控制权,所以主线程继续执行到asynResult.AsyncWaitHandle.WaitOne(10 * 1000, true),接着主线程会阻塞等待10s(因为子线程还没执行完毕,所以WaitOne让主线程阻塞等待),此时主线程的控制权被调度算法收回;

        [主线程在此阻塞等待期间,因为主线程没有运行消息循环,所以WinForm看起来卡死,不响应用户在UI中的操作]

        2、调度算法将控制权交给子线程,子线程Sleep 5s,此时子线程的控制权被调度算法收回;

        3、5s后,子线程结束了Sleep,调度算法将控制权交给子线程,然后子线程结束,子线程的控制权被调度算法收回;

        4、主线程直接结束了阻塞等待(不用再等5s,因为此时子线程已经结束了,WaitOne是最多等ns,而不是必须等ns),调度算法将控制权交给主线程,主线程执行if(asynResult.AsyncWaitHandle.WaitOne(5 * 1000, true)){}花括号中的语句(因为子线程已经执行完毕,所以if条件满足),接着执行if(){}之后的语句,然后主线程结束,主线程的控制权被调度算法收回。

IAsyncResult asynResult = myAction.BeginInvoke(null, null);asynResult.AsyncWaitHandle.WaitOne(10 * 1000, true);

会使执行该语句的当前线程等待最多10s。

2.4、去了还要回->回调

private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Func<int> myAction = new Func<int>(ExeMethod);Console.WriteLine(string.Format("Begin BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));IAsyncResult asynResult = myAction.BeginInvoke(new AsyncCallback((result) =>{Console.WriteLine(string.Format("Begin wait ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));int res = myAction.EndInvoke(result);Console.WriteLine(string.Format("End wait result:{0} ThreadId:{1} {2}", res, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));}), null);Console.WriteLine(string.Format("End BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}private int ExeMethod()
{Console.WriteLine(string.Format("Begin ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(10 * 1000);Console.WriteLine(string.Format("End ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return 25;
}

   

        1、点击Button后,主线程执行到IAsyncResult asynResult = myAction.BeginInvoke(new AsyncCallback((result) => {}), null)时,会创建一个子线程跑ExeMethod,不过此时主线程还拥有控制权,所以主线程继续执行直到主线程结束,此时主线程的控制权被调度算法收回;

        2、调度算法将控制权交给子线程,子线程Sleep 10s,此时子线程的控制权被调度算法收回;

        [子线程在Sleep期间,因为主线程有运行消息循环,所以WinForm看起来没有卡死,会时刻响应用户在UI中的操作]

        3、10s后,子线程结束了Sleep,调度算法将控制权交给子线程,子线程执行完ExeMethod后,接着执行回调函数(result) => {},然后子线程结束,此时子线程的控制权被调度算法收回。

CalcDel myCalDel = null;private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));myCalDel = new CalcDel(ExecuteTask);for (int i = 3; i < 11; i++){Console.WriteLine(string.Format("{0}-Begin BeginInvoke ThreadId:{1} {2}", i, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));myCalDel.BeginInvoke(10 * i, 1000 * i, new AsyncCallback(MyCallBack), i);Console.WriteLine(string.Format("{0}-End BeginInvoke ThreadId:{1} {2}", i, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));}Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}public delegate int CalcDel(int num, int ms);private int ExeMethod(int num, int ms)
{Console.WriteLine(string.Format("Begin ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(ms);Console.WriteLine(string.Format("End ExeMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return num * num;
}private void MyCallback(IAsyncResult result)
{Console.WriteLine(string.Format("Begin {0}-Callback ThreadId:{1} {2}", result.AsyncState.ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));int res = myCalDel.EndInvoke(result);Console.WriteLine("End {0}-Callback 结果:{1} ThreadId:{2} {3}", result.AsyncState.ToString(), res, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString());return;
}

        了解了Delegate.BeginInvoke()、Delegate.EndInvoke()的若干用法,再对比下Delegate.Invoke()。

3、Delegate.Invoke()

        Delegate.Invoke()不会创建子线程,而是直接在当前线程中运行Delegate封装的操作。

四、Control的Invoke/BeginInvoke/EndInvoke

1、基于[需求3]

此段内容,参考文章[浅谈Invoke 和 BegionInvoke的用法 - 大艺术家007 - 博客园]

1.1、禁用“检查是否跨线程访问控件”,这是不安全的做法

[待完善...]

1.2、Control.Invoke(Delegate)

private void button1_Click(object sender, EventArgs e)
{Console.WriteLine(string.Format("Begin Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread subThread = new Thread(new ThreadStart(SubThreadMethod));subThread.Start();Console.WriteLine(string.Format("End subThread.Start ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));string a = "+";for (int i = 0; i < 5; i++){a = a + "A";Console.WriteLine(string.Format("Loop-{0}-{1} ThreadId:{2} {3}", i, a, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(2000);}Console.WriteLine(string.Format("End Click ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}private void SubThreadMethod()
{Console.WriteLine(string.Format("Begin SubThreadMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));button1.Invoke(new Del(invokeMethod));//button1.BeginInvoke(new Del(DelMethod));Console.WriteLine(string.Format("End BeginInvoke ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));string b = "-";for (int i = 0; i < 7; i++){b = b + "B";Console.WriteLine(string.Format("Loop-{0}-{1} ThreadId:{2} {3}", i, b, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));Thread.Sleep(1300);}Console.WriteLine(string.Format("End SubThreadMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}public delegate void Del();private void DelMethod()
{Console.WriteLine(string.Format("Begin DelMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));this.textBox1.Text = DateTime.Now.ToString();Console.WriteLine(string.Format("End DelMethod ThreadId:{0} {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));return;
}

        在子线程中运行Control.Invoke(Delegate),会立即将Delegate封装的操作交给主线程运行。

        此时,控制权会立即从子线程交给主线程,只有当主线程运行完Delegate封装的操作后,才会再接着运行子线程中Control.Invoke(Delegate)语句后面的操作;而在主线程运行完Delegate封装的操作之前,子线程(Control.Invoke(Delegate)语句后面的操作)会被阻塞。

1.3、Control.BeginInvoke(Delegate)

        在子线程中运行Control.BeginBeginInvoke(Delegate),也会将Delegate封装的操作交给主线程运行。

        此时,子线程(Control.Invoke(Delegate)语句后面的操作)不会被阻塞。至于主线程、子线程的执行顺序,是由调度算法控制的,在用户看来是不确定的。

1.4、总结

        Control.Invoke(Delegate)、Control.BeginBeginInvoke(Delegate),都会将Delegate封装的操作交给主线程运行,而当Delegate封装的操作是刷新Control时,就不会抛出“跨线程访问控件”的运行时异常了。

五、参考

C# Winform的多线程(含Gif讲解)_白粥行的博客-CSDN博客_c# winform 多线程


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

相关文章

Invoke和BeginInvoke理解

在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate&#xff0c;至于委托的本质请参考我的另一随笔&#xff1a;对.net事件的看法。 一、为什么Control类提供了Invoke和BeginInvoke机制&#xff1f; 关于这个问题的最主要的原因已经是dotnet程序员众所周知的&…

C#的Invoke与BeginInvoke区别

【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别&#xff08;SamWang&#xff09; 今天无意中看到有关Invoke和BeginInvoke的一些资料&#xff0c;不太清楚它们之间的区别。所以花了点时间研究了下。 据msdn中介绍&#xff0c;它们最大的区别就是Be…

c# Invoke和BeginInvoke 区别详解

Control.Invoke 方法 (Delegate):在拥有此控件的基础窗口句柄的线程上执行指定的委托。 Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。 以下为实际应用中碰到的问题&#xff0c;在主线程中启动一个线程&#xff0c;然后在这个线程…

C# beginInvoke

摘要 异步这东西&#xff0c;真正用起来的时候&#xff0c;发现事情还是挺多的&#xff0c;最近在项目中用到了异步的知识&#xff0c;发现对它还是不了解&#xff0c;处理起来&#xff0c;走了不少弯路。觉得还是补一补还是很有必要的。 MSDN原文地址&#xff1a;https://ms…

[C#基础]c#中的BeginInvoke和EndEndInvoke

摘要 异步这东西,真正用起来的时候,发现事情还是挺多的,最近在项目中用到了异步的知识,发现对它还是不了解,处理起来,走了不少弯路。觉得还是补一补还是很有必要的。 MSDN原文地址:https://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx 正文 .Net framewo…

C# BeginInvoke实现异步编程

C# BeginInvoke实现异步编程 BeginInvoke实现异步编程的三种模式&#xff1a; 1.等待模式 在发起了异步方法以及做了一些其他处理之后&#xff0c;原始线程就中断并且等异步方法完成之后再继续&#xff1b; eg&#xff1a; using System; using System.Collections.Generic;…

This.invoke和this.begininvoke的区别?

应用场景 在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke和BeginInvoke就是为了解决这个问题。 个人总结 ①This.begininvoke和this.invoke注册委托调用的方法都是等UI主线程执行到“windows消息泵”的时候才…

C#——invoke和begininvoke 区别

invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱&#xff0c;这两天看了些资料&#xff0c;对这两个的用法和原理有了些新的认识和理解。 首先说下&#xff0c;invoke和begininvoke的使用有两种情况&#xff1a; 1. control中的invoke、begininvoke。…

C#Invoke和BeginInvoke应用详解

最近&#xff0c;在研究Invoke的使用&#xff0c;但是真的是一头雾水&#xff0c;网上看了很多资料&#xff0c;感觉还是看不懂&#xff0c;因为对于入门级的小白&#xff0c;想像不出Invoke的应用场景&#xff0c;更谈不上如何用了&#xff1f; 1、Invoke到底是什么&#xff…

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作为一种面向对象编程语…