C#委托同步异步说明,并比较control调用Invoke和BeginInvoke的异同
一.委托的同步和异步:
1.同步
使用Invoke调用同步,或直接写fun1("func"),在fun1.Invoke这一步会明显的阻塞线程
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static void Main( string [] args) { Thread.CurrentThread.Name = "Main" ; //定义一个带返回值的委托 var fun1 = new Func< string , int >(x => { Thread.Sleep(1000); Console.WriteLine(x); Console.WriteLine($ "ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); return 1; }); fun1.Invoke( "fun1" ); Console.WriteLine( "Main" ); Console.WriteLine($ "ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); Console.ReadKey(); } |
运行结果:
结果说明:
同步委托运行在主线程上
2.异步
使用BeginInvoke来调用异步,EndInvoke来获取返回值,AsyncCallback定义异步完成回调函数
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | static void Main( string [] args) { Thread.CurrentThread.Name = "Main" ; //定义一个带返回值的委托 var fun1 = new Func< string , int >(x => { Thread.CurrentThread.Name = "fun1" ; Console.WriteLine(x); Console.WriteLine($ "ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); return 1; }); fun1.BeginInvoke( "fun1" , t => { //var fun2 = t.AsyncState as Func<string, int>;//如果不是lambda表达式需要用该方式获得委托 //获取返回值 int ret = fun1.EndInvoke(t); Console.WriteLine($ "callback return:{ret}" ); Console.WriteLine($ "ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); }, null ); Thread.Sleep(1000); Console.WriteLine( "Main" ); Console.WriteLine($ "ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); Console.ReadKey(); } |
运行结果:
结果说明:
异步委托并不是运行在主线程上,而是运行在独立的线程上,是异步执行的
二.control的Invoke和BeginInvoke
1.Invoke
使用:
1 2 3 4 5 6 7 8 9 10 11 | private void button1_Click( object sender, EventArgs e) { Thread.CurrentThread.Name = "UIThread" ; this .Invoke( new Action(() => { Thread.Sleep(5000); Debug.WriteLine($ "Invoke ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); })); Debug.WriteLine($ "Main ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); } |
运行结果:
结果说明:
可以明显的感到Invoke阻塞了界面5s后,才执行后面的代码
Invoke内的委托在UI线程上执行,是同步的
2.BeginInvoke
使用:
1 2 3 4 5 6 7 8 9 10 11 12 | private void button1_Click( object sender, EventArgs e) { Thread.CurrentThread.Name = "UIThread" ; this .BeginInvoke( new Action(() => { Debug.WriteLine($ "BeginInvoke ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); })); Debug.WriteLine($ "Main ThreadId:{Thread.CurrentThread.ManagedThreadId} ThreadName:{Thread.CurrentThread.Name}" ); Thread.Sleep(5000); } |
运行结果:
结果说明:
可以明显看到界面卡了5s后,才先执行的BeginInvoke内的委托
结论:BeginInvoke内的委托在UI线程上执行,并不是异步,只是放在UI线程中最后执行。
三.结论
delegate.Invoke | 运行在主线程上,同步执行,并立即执行,会阻塞主线程 |
delegate.BeginInvoke | 运行在独立线程上,异步执行, 并立即执行,不会阻塞主线程 |
Control.Invoke | 运行在UI线程上,同步执行,并立即执行,会阻塞UI线程 |
Control.BeginInvoke | 运行在UI线程上,不是异步执行,等UI线程其他操作完成才执行,会阻塞UI线程 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本