我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C#线程间通信的异步机制

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C#线程间通信的异步机制

线程间通信

我们看下面的图

我们来看线程间通信的原理:线程(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 类中这个方法是没有具体的代码实现的。

到此这篇关于C#线程间异步通信的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C#线程间通信的异步机制

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

深入理解Android中的Handler异步通信机制

一、问题:在Android启动后会在新进程里创建一个主线程,也叫UI线程(非线程安全)这个线程主要负责监听屏幕点击事件与界面绘制。当Application需要进行耗时操作如网络请求等,如直接在主线程进行容易发生ANR错误。所以会创建子线程来
2022-06-06

怎么理解Java线程间通信与等待/通知机制

本篇内容主要讲解“怎么理解Java线程间通信与等待/通知机制”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解Java线程间通信与等待/通知机制”吧!一、概念简介1、线程通信在操作系统中,线
2023-06-02

C#异步通信的示例分析

这篇文章主要为大家展示了“C#异步通信的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C#异步通信的示例分析”这篇文章吧。C#异步通信概念及应用的认识首先让我们来看看:在网络编程中运用S
2023-06-17

Java学习之线程同步与线程间通信详解

这篇文章主要为大家详细介绍了线程同步和线程之间的通信的相关知识,文中的示例代码讲解详细,对我们学习Java有一定的帮助,感兴趣的可以了解一下
2022-12-27

python进程间的通信机制是什么

本文小编为大家详细介绍“python进程间的通信机制是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“python进程间的通信机制是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。进程间通信表示进程之间的数
2023-07-05

Linux下协程与协程间通信机制

在Linux环境中,协程与协程之间的通信可以通过共享内存、管道、消息队列等机制进行。以下是一些常用的通信机制:共享内存:可以通过共享内存来实现协程之间的通信。在Linux下,可以使用mmap()系统调用来创建共享内存区域,然后通过该共享内存
Linux下协程与协程间通信机制
2024-08-06

在Java中,如何实现多线程之间的同步与通信?(Java多线程编程时,应如何确保线程间的同步与有效通信?)

本文详细介绍了Java多线程编程中的同步和通信机制。为了实现同步,可以使用synchronized关键字、Lock接口或Semaphore。同步确保多个线程在访问共享资源时保持一致性。对于通信,共享内存、消息队列和管道等机制允许线程共享数据和协调任务。文中还提供了代码示例、最佳实践和问题的预防策略。通过这些机制,开发者可以创建同步且有效的Java多线程应用程序。
在Java中,如何实现多线程之间的同步与通信?(Java多线程编程时,应如何确保线程间的同步与有效通信?)
2024-04-02

C++并发编程:如何处理线程间通信?

c++++ 中线程间通信的方法包括:共享内存、同步机制(互斥锁、条件变量)、管道、消息队列。例如,使用互斥锁保护共享计数器:声明互斥锁(m)、共享变量(counter);每个线程通过加锁(lock_guard)更新计数器;确保一次只有一个线
C++并发编程:如何处理线程间通信?
2024-05-04

c语言两个线程之间怎么通信

在C语言中,可以使用以下方法实现两个线程之间的通信:1. 全局变量:两个线程可以共享一个全局变量,并通过对该变量的读写来进行通信。需要注意的是,对全局变量的读写需要加锁以避免竞争条件。示例代码:```c#include #include i
2023-10-12

Golang协程的通信机制

go 协程通过通道(发送和接收数据)和同步原语(管理对共享资源的访问)进行通信。通道用于通过发送和接收操作在协程之间传输数据。同步原语包括互斥锁(控制对共享资源的访问)、条件变量(等待条件满足后继续执行)和一次性信号(确保操作只执行一次)。
Golang协程的通信机制
2024-04-16

c语言多线程同步机制是什么

C语言中的多线程同步机制是用于确保多个线程之间互斥访问共享资源的一种机制。主要包括互斥锁(Mutex)、条件变量(Condition Variable)、信号量(Semaphore)等。1. 互斥锁(Mutex):通过对临界区(共享资源)进
2023-08-18

浅谈Node异步编程的机制

本文介绍了Node异步编程,分享给大家,具体如下: 目前的异步编程主要解决方案有:事件发布/订阅模式Promise/Deferred模式流程控制库事件发布/订阅模式 Node自身提供了events模块,可以轻松实现事件的发布/订阅//订阅
2022-06-04

C#并发编程和线程同步机制是什么

C#并发编程是指在C#程序中同时执行多个任务的能力。线程同步机制是确保多个线程能够安全地访问共享资源的方法。在C#中,线程同步可以通过以下方式实现:使用锁机制(lock):通过在代码块中使用lock关键字来锁定共享资源,确保在任意时刻只有一
C#并发编程和线程同步机制是什么
2024-03-07

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录