模板方法模式(TEMPLATE METHOD)

引言

      在c#中,可能大多数人针对于多线程之间的通讯,是熟能生巧,对于AsyncLocal 和ThreadLocal以及各个静态类中支持线程之间传递的GetData和SetData方法都是信手拈来,那多进程通讯呢,实际上也是用的比较多的地方,但是能够熟能生巧的人和多线程的相比的话呢,那还是有些差距的,所以我昨天整理了一下我所认知的几个多进程之间的通讯方式,这其中是不包括各种消息中间件以及数据库方面的,还有Grpc,WebSocket或者Signalr等方式,仅仅是以c#代码为例,c#的多进程通讯呢,大致上是分为这几类的,共享内存,借助Windows的MSMQ消息队列服务,以及命名管道和匿名管道,以及IPC HTTP TCP的Channel的方式,还有常用的Socket,借助Win32的SendMessage的Api来实现多进程通讯,还有最后一种就是多进程之间的信号量相关的Mutex,代码我会放在文章的末尾,大家有需要的话可以去下载来看看,接下来就为大家一一奉上。

共享内存

      共享内存呢,实际上c#中可以有很多种实现方式,主要是借助于Win32的Api来实现以及,使用MemoryMappedFile这个类来实现共享内存,前者需要引入多个Win32的dll的方法,后者使用起来就比较简单,只需要调用类的CreatNew方法设置好内存映射文件名称以及大小,以及操作权限就可以实现,同时支持Accessor和Stream的方式去进行读写,但是性能方面肯定是Win32的性能好,而且Win32的话不受语言的限制,至于这个类是否受限于语言,目前我是不太清楚的。接下来,咱们就看看客户端和服务端使用共享内存的方式和获取数据的代码。

      服务端:

      

复制代码

 MemoryMappedFile memoryAccessor = MemoryMappedFile.CreateNew("ProcessCommunicationAccessor", 500, MemoryMappedFileAccess.ReadWrite);//创建共享内存映射文件对象,第一个参数为映射的名称,与客户端需要对应,500为大小,单位为字节,MemoryMappedFileAccess为访问权限,是读写还是只读  只写,此处不能使用Using 否则脱离Using 就会释放,客户端无法获取到此名称的内存映射对象

            using (var accessor = memoryAccessor.CreateViewAccessor())//获取映射文件对象的视图
            {
                var helo = Encoding.UTF8.GetBytes("Accessor");
                accessor.WriteArray(0, helo, 0, helo.Length);//将给定的值写入此视图中
                richTextBox1.Text += Environment.NewLine + "Accessor Send Val:Accessor";
            }
            MemoryMappedFile memoryStream = MemoryMappedFile.CreateNew("ProcessCommunicationStream", 500, MemoryMappedFileAccess.ReadWrite);//创建流的映射文件对象
            using (var stream = memoryStream.CreateViewStream())//获取映射文件的流
            {
                var helo = Encoding.UTF8.GetBytes("Stream");
                stream.Write(helo, 0, helo.Length);//将给定的值写入此内存流中
                richTextBox1.Text += Environment.NewLine + "Accessor Send Val:Stream";
            }

复制代码

      客户端:

      

复制代码

MemoryMappedFile memoryAccessor = MemoryMappedFile.OpenExisting("ProcessCommunicationAccessor");//获取服务端定义的ProcessCommunicationAccessor名称的内存映射文件然后调用ReadArray方法读取到服务端写入的数据
            using (var accessor = memoryAccessor.CreateViewAccessor())
            {
                var s = new byte[999];
                var read = accessor.ReadArray(0, s, 0, s.Length);
                var str = Encoding.UTF8.GetString(s);
                richTextBox1.Text += Environment.NewLine + "Accessor Read Val:" + str.ToString();
            }
            MemoryMappedFile memoryStream = MemoryMappedFile.OpenExisting("ProcessCommunicationStream");//获取服务端定义的ProcessCommunicationStream名称的内存映射文件然后调用ReadToEnd方法读取到服务端写入的数据
            using (var stream = memoryStream.CreateViewStream())
            {
                using (var reader = new StreamReader(stream))
                {
                    var str = reader.ReadToEnd();
                    richTextBox1.Text += Environment.NewLine + "Stream Read Val:" + str + "\r\n";
                }
            }

复制代码

     可以看到我们在服务端定义了一个是Accessor类型的MemoryMappedFile在写入数据的时候是用MemortViewAccessor的方式去写入的,然后又定义了一个使用Stream的方式去进行写入数据,在客户端中,我们直接使用OpenExisting方法去判断是否存在这个对象,如果存在的话,就使用了服务端定义的CreatNew这个对象,如果不存在则是Null,当然了也可以使用其他的方式去进行获取,例如CreateOrOpen判断是否是获取的还是重新创建的方式,我们在客户端使用ReadArray和ReadToEnd的方式读取了服务端写入的Accessor和Stream的数据,然后我们就可以在客户端和服务端之间进行一个数据传输的一个通讯。

Windows的MSMQ

      使用MSMQ的前提是需要在本计算机安装了消息队列,安装方式需要在控制面板,程序和功能那里启用或关闭程序,在列表中找到我们需要的消息队列(MSMQ)服务器然后安装,安装完成后,我们点击我的电脑右键管理找到最下面的服务和应用程序就可以看到我们安装的消息队列了,然后找到专用队列,我们在这里新建一个队列,然后就可以在我们的代码中使用了,这里呢我只是简单写一个示范,实际上在Messaging命名空间里,还支持对消息队列权限的控制,等等的操作,接下来我们看看如何在代码中使用消息队列。

    服务端中我们定义了我们需要使用的消息队列的类型以及名称,名称规范的话也可以参考官网对名称定义的介绍,还支持其他方式名称的定义,定义好之后呢,我们便发送了一个消息Message HelloWorld的一条消息

    

MessageQueue queue = new MessageQueue(".\\Private$\\MessageQueue");//右键我的电脑,点击管理 找到服务和应用程序找到专用队列,创建的专用队列名称就是MessageQueue
            queue.Send("Message HelloWorld");//然后发送消息
            richTextBox1.Text += Environment.NewLine + "MessageQueue Send Val:Message HelloWorld";

   客户端中,我们也是和服务端定义了一个消息队列的一个对象,然后我们监听这个消息队列的收到消息的事件,开始异步接收消息,在接收完毕之后呢,会走到我们写的ReceiveCompleted的完成事件中,然后我们结束异步接收的,获取到服务端发送的消息,然后使用XmlMessageFormatter对象去格式化我们服务端发送的消息,这里的Type是服务端发送的消息类型,两者需要对应,在接受并展示到UI之后,我们在开始异步接收。

复制代码

 var context = WindowsFormsSynchronizationContext.Current;
            MessageQueue myQueue = new MessageQueue(".\\Private$\\MessageQueue");//定义消息队列对象,和服务端的地址一样,
            myQueue.ReceiveCompleted += (a, b) =>//定义接受完成的时间
            {
                var cts = context;
                var queue = a as MessageQueue;//队列对象
                queue.EndReceive(b.AsyncResult);
                var msg = b.Message;//接收到的消息对象
                msg.Formatter = new XmlMessageFormatter() { TargetTypes = new Type[] { typeof(string) } };//设置接收到的消息使用什么方式格式化
                var msgVal = msg.Body;//此处是服务端发送的具体的消息对象
                cts.Send(new System.Threading.SendOrPostCallback(s =>
                {

                    richTextBox1.Text += Environment.NewLine + "MessageQueue Read Val:" + msgVal + "\r\n";
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值