线程间通信

  我们看下面的图

 我们来看线程间通信的原理:线程(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. java接入微信JS-SDK

    在微信公众号开发中不可,jssdk的接入虽然不是必须,但是根据业务需求我们还是可能用到,下面是自己整理的关于java接入的jssdk的方法,这里是记录关于接入微信JS-SDK的准备工作,关于接入JS- ...

  2. lambda表达式笔记

    前几天一位好友分享了一篇文章,其中讲到了lambda表达式,正好最近看了一些内容,就做做笔记吧... lambda表达式服务于函数式接口,如果需要一个函数式接口的对象时,就可以用lambda表达式代替 ...

  3. Java开发笔记(一百四十)JavaFX的选择框

    与Swing一样,JavaFX依然提供了三种选择框,它们是复选框CheckBox.单选按钮RadioButton.下拉框ComboBox,分别说明如下: 一.复选框CheckBox复选框允许同时勾选多 ...

  4. Delphi百度语音【支持语音识别和语音合成】

    作者QQ:(648437169) 点击下载➨百度语音         语音识别api文档         语音合成api文档 [Delphi 百度语音]支持获取 Access Token.语音识别.语 ...

  5. python模块知识三 hashlib 加密模块、collections、re模块

    8.hashlib 加密模块 ​ 主要用于加密和校验 常见密文:md5,sha1,sha256,sha512 只要明文相同,密文就相同 只要明文不相同,密文就是不相同的 不能反逆(不能解密)--md5 ...

  6. 15.Python略有小成(内置模块Ⅰ)

    Python(内置模块Ⅰ) 一.序列化模块(很重要) 序列化 : 将一种数据结构(,list,dict...),转化成一个特殊的字符串的过程 我们现在要解决的问题 : 如果有一种特殊的字符串,这个字符 ...

  7. 我在LeetCode的首次刷题

    到现在为止,我才发现我的博客一篇感受,心得,体会之言都没有. 今天就来随便扯扯. 刷题,是我最近一直在干的事情.也就每天写一两个.忘了就没写这种.也收藏了好几个刷题网站,当然第一次接触肯定是 WUST ...

  8. dubbo线程池的拒绝策略

    jdk自带的原生的拒绝策略抛出的异常信息不够详细,而dubbo对拒绝策略进行了改写,抛出的信息更具有参考价值,值得我们借鉴. jdk自带的原生拒绝策略抛出的信息: // ThreadPoolExecu ...

  9. gitlab-runner 的 executors 之 docker

    gitlab-runner 的 executors 之 docker GitLab Runner 实现了许多执行程序,可用于在不同的场景中运行构建.所有执行程序分别为: SSH Shell Paral ...

  10. Django:母版、继承、组件、自定义标签

    1.for循环应用 1.1for Variable Description forloop.counter 当前循环的索引值(从1开始) forloop.counter0 当前循环的索引值(从0开始) ...