编程环境要求:VS2008/FX2.0

众所周知,从VS2005/FX2.0起,在多线程环境下是不允许跨线程修改主线程上窗口控件的。

例如:

private void button1_Click(object sender, EventArgs e)
{
    Thread t = new Thread(new ThreadStart(CrossThreadCall));
    t.Start();
}
public void CrossThreadCall()
{
    Text = "test";
}

将直接导致异常:
未处理 System.InvalidOperationException
  Message="线程间操作无效: 从不是创建控件“Form1”的线程访问它。"
  Source="System.Windows.Forms"
  StackTrace:
       在 System.Windows.Forms.Control.get_Handle()
       在 System.Windows.Forms.Control.set_WindowText(String value)
       在 System.Windows.Forms.Form.set_WindowText(String value)
       在 System.Windows.Forms.Control.set_Text(String value)
       在 System.Windows.Forms.Form.set_Text(String value)
       在 delegate_test1.Form1.CrossThreadCall() 位置 f:\app\delegate_test1\delegate_test1\Form1.cs:行号 26
       在 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       在 System.Threading.ThreadHelper.ThreadStart()

通用的解决方法是使用Control.Invoke方法来调用一个Delegate,从而安全地跨线程调用。

例如:

public void CrossThreadCall()
{
    Invoke(new void_d(CrossThreadCall_Local));
}
void CrossThreadCall_Local()
{
    Text = "test";
}
public delegate void void_d();

但是这样的缺点是要不得不为每个调用编写一个Invoke跳板,还要额外声明一个委托类型,实在不怎么优雅。

于是我们想到用匿名函数来写。我的第一反应是:

Invoke(delegate { Text = "test"; });

可惜不行。编译压根就没通过,写着:
无法将 匿名方法 转换为类型“System.Delegate”,因为它不是委托类型

无语,delegate竟然不是委托类型?

等我把Google翻了一遍后,找到了答案。

The problem the user is seeing is that the Thread ctor accepts a specific delegate -- the ThreadStart delegate.  The C# compiler will check and make sure your anonymous method matches the signature of the ThreadStart delegate and, if so, produces the proper code under-the-covers to create the ThreadStart delegate for you.

But Control.Invoke is typed as accepting a "Delegate".  This means it can accept any delegate-derived type.  The example above shows an anonymous method that has a void return type and takes no parameters.  It's possible to have a number of delegate-derived types that match that signature (such as MethodInvoker and ThreadStart -- just as an example).  Which specific delegate should the C# compiler use?  There's no way for it to infer the exact delegate type so the compiler complains with an error.

也就是说,对于Thread.ctor()来说,由于接受的是一个ThreadStart委托,编译器便可以将匿名函数与ThreadStart委托类型匹配,最后能够正确编译。
而对于Control.Invoke()来说,任何的代理类型都是可接受的,也就是说ThreadStart和MethodInvoker都是可以接受的类型。这样编译器反而不知道应该用哪个代理去匹配匿名函数了,导致了编译错误的发生。

知道了原因,问题就很容易解决了。我们只需要加上MethodInvoker这个wrapper就能使用匿名函数了。

Invoke(new MethodInvoker(delegate { Text = "test"; }));

或者更简单地,用Lambda表达式来解决问题:

Invoke(new MethodInvoker(() => Text = "test"));

希望本文能够帮助有同样困惑的朋友。

转载于:https://www.cnblogs.com/msg7086/articles/1266096.html

心得 如何优雅地跨线程修改主线程窗口控件相关推荐

  1. MFC子线程访问主线程对话框程序的控件对象

    最近在使用 VC 开发软件时需要用到多线程同步来解决开发过程中遇到的问题.本来以为只要象控制台程序一样,在主线程创建子线程,并设置好相应的对象事件就能解决问题,但是等到真正做起来,才在实践中发现原来事 ...

  2. C#-WinForm跨线程修改UI界面

    背景 在我做WinForm开发的过程中,经常会遇到耗时操作或阻塞操作.他们会引发软件的卡顿甚至假死,严重影响软件的使用.因此,这类耗时或阻塞的操作一般都会使用异步的方式去执行,不影响主线程(UI线程) ...

  3. DLL内线程同步主线程研究(子线程代码放到主线程执行)

    DLL内线程同步主线程研究(子线程代码放到主线程执行) 我们在实际项目中经常会用到多线程编程,比如Socket编程等,在创建的线程内同步主线程一般使用Synchronize方法实现子线程操作放到主线程 ...

  4. QT子线程与主线程的信号槽通信

    最近用QT做一个服务器,众所周知,QT的主线程必须保持畅通,才能刷新UI.所以,网络通信端采用新开线程的方式.在涉及到使用子线程更新Ui上的控件时遇到了点儿麻烦.网上提供了很多同一线程不同类间采用信号 ...

  5. WinForm中新开一个线程操作窗体上的控件(跨线程操作控件)GOOD

    http://www.cnblogs.com/joey0210/p/3450379.html 最近在做一个winform的小软件(抢票的...).登录窗体要从远程web页面获取一些数据,为了不阻塞登录 ...

  6. android判断主线程_Android主线程和子线程区别详解

    主线程和子线程的区别 每个线程都有一个唯一标示符,来区分线程中的主次关系的说法. 线程唯一标示符:Thread.CurrentThread.ManagedThreadID; UI界面和Main函数均为 ...

  7. java 父线程_Java父线程(或是主线程)等待所有子线程退出的实例

    导读热词 实例如下: static void testLock1(){ final AtomicInteger waitCount = new AtomicInteger(30000); final ...

  8. C#中子线程操作主线程中窗体上控件的方法

    Demo this.listView1.Visible = true;this.listView1.BeginUpdate();this.listView1.EndUpdate(); //结束数据处理 ...

  9. Qt自定义事件实现及子线程向主线程传送事件消息

    近期在又一次学习Qt的时候,由于要涉及到子线程与主线程传递消息,所以便琢磨了一下.顺便把有用的记录下来,方便自己以后查询及各位同仁的參考! 特此声明,本篇博文主要讲述有用的,也就是直接说明怎么实现,就 ...

最新文章

  1. 分享Kali Linux 2016.2第46周虚拟机
  2. flask的同一ip域名不同端口的两个网站session冲突
  3. 回溯---分割字符串使得每个部分都是回文数
  4. DNS服务(4)Slave DNS及高级特性
  5. Multi GET  API
  6. Python基础(循环控制语句break/continue)
  7. html展开显示样式,html a title 自定义样式显示
  8. monkey入门研究
  9. 流媒体协议RTP、RTCP、H264详解
  10. 有什么软件可以免费下载歌曲?99%不知道这3款软件!
  11. 腾讯的职级系统——看清自己的职场宿命
  12. yield 函数的理解
  13. 20230214不是情人的情人节
  14. pandas read_excel 和 to_excel 读写Excel的参数详解
  15. ISO文件怎么安装?
  16. JavaEE企业级实战项目 智牛股第四天 NACOS、ceph集群和Netty
  17. 审核工作流程图、在线流程图、审批流程设计、在线绘图
  18. 异军突起,私域流量才是真正的护城河(中)
  19. 周金瑞10.31现货黄金、白银TD、美原油开盘操作建议
  20. 最全最详细!请收下这份电路反馈基础知识秘

热门文章

  1. Oracle Golden Gate 系列十三 -- 配置GG进程检查点(checkpoint) 说明
  2. cuda安装配置VS2013
  3. org.apache.jasper.JasperException: An exception occurred processing JSP page /admin/jiaoshi/daochuEx
  4. windows调用python_如何在Windows操作系统中从R调用Python?
  5. vue项目中使用mock(二)
  6. 少儿编程100讲轻松学python(七)-pycharm怎么删除项目
  7. 少儿编程150讲轻松学Scratch(二)-制作过马路小游戏
  8. matlab_ga(),matlab遗传算法ga函数
  9. rocksdb原理_[转]Rocksdb Compaction原理
  10. Vue+ECharts的小示例