线程间通信

  我们看下面的图

 我们来看线程间通信的原理:线程(Thread B)和线程(Thread A)通信, 首先线程A 必须实现同步上下文对象(Synchronization Context), 线程B通过调用线程A的同步上下文对象来访问线程A,所有实现都是在同步上下文中完成的.线程B有两种方式来实现线程间的通信.

  第一种:调用线程A的同步上下文对象,阻碍当前线程,执行红色箭头调用,直到黄色箭头返回(同步上下文执行完毕)才释放当前线程. (1->2->3->5)

  第二种:调用线程A的同步上下文对象(实际上是在开启一个新线程去执行,1->2->3->5) ,执行红色箭头,但并不阻碍当前线程(原有线程,1->4->5),绿色箭头继续执行.

  文章中将会通过下面几个类来进行介绍:

    ISynchronizeInvoke 接口

    SynchronizationContext 类

    AsyncOperation / AsyncOperationManager 类

1. ISynchronizeInvoke 接口

  我们先来看下面一段异步的代码(Window Form控件下有1个Button/1个Label),但点击Button的时候,执行异步调用,完成后,告诉Window Form的 Label控件Text属性” Asynchronous End”.

在windows应用窗体应用程序中,对窗体上控件属性的任何修改都必须在主线程中完成。不能从其他线程安全地访问控件的方法和属性。

ISynchronizeInvoke 接口来自.Net Framework 1.0,提供3个方法1个属性:

  •   BeginInvoke / EndInvoke 方法 : 异步方法
  •   Invoke 方法 : 同步方法
  •   InvokeRequired 属性 : 判读来源的执行线程
        delegate void DoWork();
private void button1_Click(object sender, EventArgs e)
{
//更新状态,添加到Listbox 中
AddValue("Asynchronous Start.");
//使用委托来调用异步方法
DoWork work = DoWorkMethod;
work.BeginInvoke(OnWorkCallback, work);
} void OnWorkCallback(IAsyncResult asyncResult)
{
DoWork work = asyncResult.AsyncState as DoWork;
if (work != null)
{
work.EndInvoke(asyncResult);
}
//(1)方法:调用Control控件的Invoke
//Action<string> asyncUpdateState = UpdateStatus; //Action<string> 介绍=> 附1
//Invoke(asyncUpdateState, "1:Asynchronous End."); //(2)方法:直接在异步调用的线程下
UpdateStatus("2:Asynchronous End.");
} void UpdateStatus(string input)
{
//把你需要通知的控件Control 赋值给ISynchronizeInvoke
//来实现线程间的通信
ISynchronizeInvoke async = this.listBoxStatus;
//使用(1)方法,InvokeRequired == false ,来源当前(Window Form)主线程
if (async.InvokeRequired == false)
AddValue(input);
else// 使用(2)方法 == true ,来源其他线程(异步)
{
Action<string> action = new Action<string>(status =>
{
AddValue(status);
});
//调用ISynchronizeInvoke 提供的Invoke 同步方法,阻碍线程,直到调用结束
//也可以使用ISynchronizeInvoke 提供的异步BeginInvoke/EndInvoke方法来实现调用.
async.Invoke(action, new object[] { input });
}
} void AddValue(string input)
{
this.listBoxStatus.Items.Add(string.Format("[(#{2}){0}]Context is null:{1}", input, Thread.CurrentContext == null, Thread.CurrentThread.ManagedThreadId));
}
void DoWorkMethod()
{
Thread.Sleep(3000);//模拟耗时工作
}

在代码中(UpdateStatus方法体内),我们可以看到主要是在ISynchronizeInvoke async = this.listBoxStatus;实现了线程间的通信,MSDN的解释” 实现此接口的对象可以接收事件已发生的通知,并且可以响应有关该事件的查询”. 并使Window Form(主线程) 下的ListBox 控件和来自异步方法(另外一个线程)的建立了通道.
InvokeRequired 判断线程的来源,

如果使用(1)方法,来源于Window Form 自身Control 的Invoke方法, InvokeRequired将返回false; 来源另外线程(异步)。

如果使用(2)返回true.同时ISynchronizeInvoke 提供了异步(BeginInvoke+EndInvok)和同步方法(Invoke)来实现线程间通信.Invoke 就是最上面的图1 所示的第一种 / BeginInvoke+EndInvok 是第二种.

2. SynchronizationContext 类

  相比ISynchronizeInvoke 接口,SynchronizationContext 类(来自.Net Framework 2.0)提供了更多的方法来操作同步上下文对象,实现线程间通信.在上面的例子中SynchronizationContext类中将由 Post/Send 方法来实现.

  反编译后我们看到:

 public virtual void Post(SendOrPostCallback d, object state)
 {
   ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state);
 }  public virtual void Send(SendOrPostCallback d, object state)
 {
   d(state);
 }
  • Send = ISynchronizeInvoke 中的Invoke 同步调用.图1中的第一种
  • Post = ISynchronizeInvoke 中的BeginInvoke + EndInvoke异步调用. 图1中的第二种

SynchronizationContext 类举例(在WinForm 下编程)

     delegate void DoWork();
   private void button1_Click(object sender, EventArgs e)
   {
     //System.Windows.Forms.Form 自动的创建默认的同步上下文对象,
     //直接的获取当前的同步上下文对象
     SynchronizationContext context = SynchronizationContext.Current;
     //更新状态,添加到Listbox 中
     AddValue<string>("Asynchronous Start.");
     //使用委托来调用异步方法
      DoWork work = DoWorkMethod;
      work.BeginInvoke(OnWorkCallback, context);     }
    void OnWorkCallback(IAsyncResult asyncResult)
    {
      AsyncResult async = (AsyncResult)asyncResult;
      DoWork work = (DoWork)async.AsyncDelegate;
      work.EndInvoke(asyncResult);
      
      //更新状态
      UpdateStatus("Asynchronous End.", asyncResult.AsyncState);
    }
    void UpdateStatus(object input,object syncContext)
    {
      //获取主线程(Window Form)中同步上下文对象
      SynchronizationContext context = syncContext as SynchronizationContext;
      //使用SynchronizationContext 类中异步Post 方法
      SendOrPostCallback callback = new SendOrPostCallback(p => {
        AddValue<object>(p);
      });
      context.Post(callback, input);//Post 为异步,Send 为同步     }
    void AddValue<T>(T input)
    {
      this.listBoxStatus.Items.Add(string.Format("[(#{2}){0}]Context is null:{1}", input, Thread.CurrentContext == null, Thread.CurrentThread.ManagedThreadId));
    }
    void DoWorkMethod()
    {
      Thread.Sleep(3000);//模拟耗时工作
    }

上面我们已经说过在主线程中System.Windows.Forms.Form 自动的创建默认的同步上下文对象, 这时候我们把当前的同步上下文对象通过参数的形式赋值到异步线程中,调用Post 方法来实现, Post 方法接收 SendOrPostCallback 委托和额外object state参数,在Post方法体内调用线程池的线程来实现(Code2.1).当然我们也可以直接使用Send方法. 

下面我们看看线程中的代码(在Console 下编程).

static class Program
 {
   static void Main()
   {
     Output("Main Thread Start.");
     //为主线程创建Synchronization Context
     var context = new SynchronizationContext();
     //开始一个新线程
     Thread threadB = new Thread(work);
      threadB.Start(context);       Console.Read();
    }
    static void work(object context)
    {
      Output("Thread B");       //获取主线程中的同步上下文对象
      SynchronizationContext sc = context as SynchronizationContext;       //异步的方式和主线程通信,并发送"Hello World".
      sc.Post(new SendOrPostCallback(p =>
      {
        Output(p);
      }), "Hello World");
    }
    static void Output(object value)
    {
      Console.WriteLine("[ThreadID:#{0}]{1}", Thread.CurrentThread.ManagedThreadId, value);
    }
  }

 

在主线程中因为没有同步上下文对象,所以开始我们new SynchronizationContext(); 对象,其他和上面基本一样.此例说明了Post 是开启新线程(线程池)运行的.

3. AsyncOperation / AsyncOperationManager 类

  AsyncOperation / AsyncOperationManager 类是SynchronizationContext 类的进一步封装和实现, AsyncOperationManager在创建AsyncOperation对象的时候会取得当前线程的同步上下文对象,并存储在AsyncOperation之中,使我们访问同步上下文对象更加容易.

public class MySynchronizedClass
 {
   private AsyncOperation operation;
   public event EventHandler somethingHappened;
   public MySynchronizedClass()
   {
     //创建AsyncOperation 对象,并把当前线程的同步上下文保持到AsyncOperation中.
     operation = AsyncOperationManager.CreateOperation(null);       Thread workerThread = new Thread(new ThreadStart(DoWork));
      workerThread.Start();
    }     private void DoWork()
    {
      SendOrPostCallback callback = new SendOrPostCallback(state =>
       {
         EventHandler handler = somethingHappened;          if (handler != null)
         {
           handler(this, EventArgs.Empty);
         }
       });       operation.Post(callback, null);
      //注意1
      operation.OperationCompleted();
    }
  }

AsyncOperation类中实现了OperationCompleted的方法. SynchronizationContext 类中这个方法是没有具体的代码实现的。

Net线程间通信的异步机制的更多相关文章

  1. Java 线程间通信 —— 等待 / 通知机制

    本文部分摘自<Java 并发编程的艺术> volatile 和 synchronize 关键字 每个处于运行状态的线程,如果仅仅是孤立地运行,那么它产生的作用很小,如果多个线程能够相互配合 ...

  2. Java多线程基础——线程间通信

    在使用多线程的时候,经常需要多个线程进行协作来完成一件事情.在前面两章分析了Java多线程的基本使用以及利用synchronized来实现多个线程同步调用方法或者执行代码块.但上面两章的内容涉及到的例 ...

  3. Android线程间通信机制——深入理解 Looper、Handler、Message

    在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...

  4. 《java多线程编程核心技术》不使用等待通知机制 实现线程间通信的 疑问分析

    不使用等待通知机制 实现线程间通信的 疑问分析 2018年04月03日 17:15:08       ayf 阅读数:33 编辑 <java多线程编程核心技术>一书第三章开头,有如下案例: ...

  5. linux高级编程基础系列:线程间通信

    linux高级编程基础系列:线程间通信 转载:原文地址http://blog.163.com/jimking_2010/blog/static/1716015352013102510748824/ 线 ...

  6. Java多线程编程(三)线程间通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  7. 线程间通信 GET POST

    线程间通信有三种方法:NSThread   GCD  NSOperation       进程:操作系统里面每一个app就是一个进程. 一个进程里面可以包含多个线程,并且我们每一个app里面有且仅有一 ...

  8. Java多线程编程核心技术---线程间通信(一)

    线程是操作系统中独立的个体,但这些个体如果不经过特殊处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一.线程间通信可以使系统之间的交互性更强大,在大大提高CPU利用率的同时还会使程序员对各 ...

  9. 【原】iOS多线程之线程间通信和线程互斥

    线程间通信 1> 线程间通信分为两种 主线程进入子线程(前面的方法都可以) 子线程回到主线程 2> 返回主线程 3> 代码 这个案例的思路是:当我触摸屏幕时,会在子线程加载图片,然后 ...

随机推荐

  1. SpringBoot系列教程web篇之Beetl环境搭建

    前面两篇分别介绍了目前流行的模板引擎Freemaker和Thymeleaf构建web应用的方式,接下来我们看一下号称性能最好的国产模板引擎Beetl,如何搭建web环境 本文主要来自官方文档,如有疑问 ...

  2. LeetCode 611. 有效三角形的个数(Valid Triangle Number)

    611. 有效三角形的个数 611. Valid Triangle Number 题目描述 LeetCode LeetCode LeetCode611. Valid Triangle Number中等 ...

  3. LeetCode 496. 下一个更大元素 I(Next Greater Element I) 35

    496. 下一个更大元素 I 496. Next Greater Element I 题目描述 给定两个没有重复元素的数组 nums1 和 nums2,其中 nums1 是 nums2 的子集.找到  ...

  4. pychram 中 Terminal 中 git log 中文乱码解决办法

    添加环境变量 set LESSCHARSET=utf-8 执行以下命令 git config --global core.quotepath false 不成功执行以下命令 git config -- ...

  5. 《学渣的电子技术自学笔记》——二极管的工作频率与PN结结面积的关系

    <学渣的电子技术自学笔记>--二极管的工作频率与PN结结面积的关系 书本原文 :按结构分,二极管有点接触型.面接触型和平面型三类.点接触型二极管(一般为锗管)的PN结结面积很小(结电容小) ...

  6. Java代码生成器Easy Code

    EasyCode是基于IntelliJ IDEA开发的代码生成插件,支持自定义任意模板(Java,html,js,xml).只要是与数据库相关的代码都可以通过自定义模板来生成.支持数据库类型与java ...

  7. LOJ2484 CEOI2017 Palindromic Partitions DP、回文树

    传送门 当我打开Luogu题解发现这道题可以Hash+贪心的时候我的内心是崩溃的-- 但是看到这道题不都应该认为这是一道PAM的练手好题么-- 首先把原字符串重排为\(s_1s_ks_2s_{k-1} ...

  8. string.join用法

    C# String.Join用法 String.Join(String, String[]) 在指定 String 数组的每个元素之间串联指定的分隔符 String,从而产生单个串联的字符串 例如: ...

  9. java之struts2之OGNL表达式

    struts2推荐使用ognl表达式 ognl: object graph navigation language 对象导航图语言 如:school.teacher.address="北京& ...

  10. HTML知识整理

    以下是自己对以前所学的部分HTML相关知识进行的简单的梳理,水平有限,若有问题的地方,还请见谅. 1. 常用的浏览器及浏览器内核分别是什么? IE:Trident 内核 Firefox:gecko 内 ...