在研究c# 线程之间通信时,发现传统的方法大概有三种

①全局变量,由于同一进程下的多个进程之间共享数据空间,所以使用全局变量是最简单的方法,但要记住使用volatile进行限制。

②线程之间发送消息(这个随后文章中会讨论到)。

③ CEvent为MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步,这个主要是实现线程直接同步的一种方法。

本文介绍的一种方法是这三种之外的一种方法,本文中实例是通过创建一个线程类,通过委托事件把值传送到Form所在的类中,同时更新Form类中的一个控件(Label)中的值。

实现功能比较简单,目的是实现此功能,先把代码贴上:

MyThread.cs

  1. using System;
  2. using System.Threading;
  3. namespace ThreadsComm
  4. {
  5. public delegate void ReadParamEventHandler(string sParam);
  6. class MyThread
  7. {
  8. public Thread thread1;
  9. private static ReadParamEventHandler OnReadParamEvent;
  10. public MyThread()
  11. {
  12. thread1 = new Thread(new ThreadStart(MyRead));
  13. thread1.IsBackground = true;
  14. thread1.Start();
  15. }
  16. public event ReadParamEventHandler ReadParam
  17. {
  18. add { OnReadParamEvent += new ReadParamEventHandler(value);}
  19. remove{ OnReadParamEvent -= new ReadParamEventHandler(value);}
  20. }
  21. protected void MyRead()
  22. {
  23. int i = 0;
  24. while (true)
  25. {
  26. Thread.Sleep(1000);
  27. i = i + 1;
  28. OnReadParamEvent(i.ToString());//触发事件
  29. }
  30. }
  31. }
  32. }

其中的

  1. public event ReadParamEventHandler ReadParam
  2. {
  3. add { OnReadParamEvent += new ReadParamEventHandler(value);}
  4. remove{ OnReadParamEvent -= new ReadParamEventHandler(value);}
  5. }

这个需要说明一下:

add 上下文关键字用于定义一个自定义事件访问器,当客户端代码订阅您的事件时将调用该访问器。 如果提供自定义 add 访问器,还必须提供 remove 访问器。

remove 上下文关键字用于定义一个自定义事件访问器,当客户端代码取消订阅事件时将调用该访问器。 如果提供自定义 remove 访问器,还必须提供 add 访问器。
Form.cs

  1. using System;
  2. using System.Windows.Forms;
  3. namespace ThreadsComm
  4. {
  5. public partial class Form1 : Form
  6. {
  7. private static string param = "";
  8. public Form1()
  9. {
  10. InitializeComponent();
  11. MyThread thread1 = new MyThread();
  12. thread1.ReadParam += this.OnRead;
  13. }
  14. private void OnRead(string sParam)
  15. {
  16. param = sParam;
  17. Object[] list = { this,System.EventArgs.Empty};
  18. this.lblShow.BeginInvoke(new EventHandler(LabelShow), list);
  19. }
  20. protected void LabelShow(Object o, EventArgs e)
  21. {
  22. this.lblShow.Text = param;
  23. }
  24. }
  25. }

其中的,

  1. MyThread thread1 = new MyThread();
  2. thread1.ReadParam += this.OnRead;

是订阅线程类中的事件。

  1. this.lblShow.BeginInvoke(new EventHandler(LabelShow), list);

Invoke或者 BeginInvoke方法都需要一个委托对象作为参数。委托类似于回调函数的地址,因此调用者通过这两个方法就可以把需要调用的函数地址封送给界面线程。这些方法里面如果包含了更改控件状态的代码,那么由于最终执行这个方法的是界面线程,从而避免了竞争条件,避免了不可预料的问题。如果其它线程直接操作界面线程所属的控件,那么将会产生竞争条件,造成不可预料的结果。

使用 Invoke完成一个委托方法的封送,就类似于使用 SendMessage方法来给界面线程发送消息,是一个同步方法。也就是说在 Invoke封送的方法被执行完毕前, Invoke方法不会返回,从而调用者线程将被阻塞。

使用 BeginInvoke方法封送一个委托方法,类似于使用 PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用 EndInvoke方法或者其它类似 WaitHandle机制等待异步操作的完成。

但是在内部实现上, Invoke和 BeginInvoke都是用了 PostMessage方法,从而避免了 SendMessage带来的问题。而 Invoke方法的同步阻塞是靠 WaitHandle机制来完成的。

想实验的读者可以建一个winform工程,采用上边的代码试验一下。

参考文档及推荐阅读:

Invoke与BeginInvoke

c#多线程通信之委托(事件)的更多相关文章

  1. 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程

    额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...

  2. C# 委托/事件本质详解

    委托 一.什么是委托 IL层面1>委托的本质就是一个类2>继承自System.MulticastDelegate3>委托里面内置了3个方法:Invoke(),BeginInvoke( ...

  3. C# ~ 从 委托事件 到 观察者模式 - Observer

    委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分: 参考 [1]. 初识事件 到 自定义事件: [2]. 从类型不安全的委托 到 类型安全的事件: [3]. 函数指针 ...

  4. Android多线程通信机制

    掌握Android的多线程通信机制,我们首先应该掌握Android中进程与线程是什么. 1. 进程 在Android中,一个应用程序就是一个独立的进程(应用运行在一个独立的环境中,可以避免其他应用程序 ...

  5. C#委托,事件理解入门 (译稿)

    原文地址:http://www.codeproject.com/Articles/4773/Events-and-Delegates-Simplified 引用翻译地址:http://www.cnbl ...

  6. 关于ios使用jquery的on,委托事件失效

    $('.parents').on("click",'.child',function(){}); 类似上面这种,在ios上点击"child"元素不会起作用,解决 ...

  7. Observer设计模式中-委托事件-应用在消息在窗体上显示

    Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...

  8. 转:windows下多线程通信方法

    多线程知识简介 同一进程中可以包含多个线程,由于进程中的多个线程可以共享进程中的资源,所以使同一进程中的多个线程之间通信相对比较简单. 当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前 ...

  9. Asp.net用户控件和委托事件

    在Asp.net系统制作过程中,门户类型的网站,我们可以用DIV+CSS+JS+Ajax全部搞定,但是一旦遇到界面元素比较复杂的时候,还是UserControl比较方便一些,各种封装,各种处理,然后拖 ...

随机推荐

  1. Error:Execution failed for task ':app:validateSigningDebug'.

    今天出差回来 第一天   把项目重新移植到新的电脑上 一运行 给我报错了 ! 这个是签名的路径错误  我们需要重新导一下路径就可以了 点击左上角 File ->  project structu ...

  2. VS2010-MFC(对话框:为对话框添加控件)

    转自:http://www.jizhuomi.com/software/151.html 上一讲创建了一个名为“Addition”的工程,目的是生成一个实现加法运算的应用程序.实现加法计算有几个必要的 ...

  3. SpringBoot学习笔记(六):SpringBoot实现Shiro登录控制和权限控制

    登录模块:在登录时必须调用 授权模块:不是一登录就调动,而是当角色权限控制时才会调用 登录控制 环境搭建在上一篇. 数据库表 表名:role 字段:id rolename 表名:user 字段:id ...

  4. Logback 日志组件的使用

    Logback 是由 log4j 创始人设计的又一个开源日志组件. 一. logback 的介绍 ​ logback 当前分成三个模块:logback-core,logback- classic 和 ...

  5. Cesium官方教程12--材质(Fabric)

    原文地址:https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric 介绍 Fabric 是Cesium中基于JSON格式来描述materi ...

  6. Struts2入门问题

    一 使用Struts 2 开发程序的基本步骤 加载Struts2 类库 配置web.xml文件 开发视图层页面 开发控制层Action 配置struts.xml文件 部署.运行项目 第一步先导架包:在 ...

  7. sqlmap:入门(手工注入)

    一. 联合查询注入union(less-1) 1. union操作符用于合并两个或多个select语句结果集: 2. union后的select语句必须拥有和最前的select语句拥有相同数量的字段, ...

  8. How to ping and test for a specific port from Linux or Unix command line

    Use telnet command The syntax is:telnet {host} {port}telnet www.cyberciti.biz 80telnet 192.168.2.254 ...

  9. selenium简单应用

    文章引用自:https://wenku.baidu.com/view/d5c296c75727a5e9846a6182.html 例子:

  10. jquery刷新局部和全页的方法

    一.全页面刷新方法: window.location.reload()刷新当前页面. parent.location.reload()刷新父亲对象(用于框架) opener.location.relo ...