通过命名管道访问单例会创建第二个单例。未在主机端触发的事件
Posted
技术标签:
【中文标题】通过命名管道访问单例会创建第二个单例。未在主机端触发的事件【英文标题】:Access Singleton via Named pipe creates second singleton. Events not fired on host side 【发布时间】:2017-02-15 10:28:50 【问题描述】:我有两个进程,第二个进程需要在第一个进程中访问单例。所以我写了一个服务器来帮助共享实例。
但有些问题,似乎客户端获得了自己的单例版本,而不是原始实例。
最小的例子来自两个项目。这是客户端:
Program
using IPCServer;
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;
namespace IPCEventTest
class IPCClient
static void Main(string[] args)
Process ipcserver = Process.Start("IPCServer.exe");
Thread.Sleep(2000);
Console.WriteLine($"DateTime.Now.ToUniversalTime() Main Start Connect");
Module module = Connect("WellKnownName"); //this name is used by the host
Console.WriteLine($"DateTime.Now.ToUniversalTime() Main End Connect");
Console.WriteLine($"DateTime.Now.ToUniversalTime() Main Start Raise");
module.RaiseEvent(); //raise event should raise within the server process
Console.WriteLine($"DateTime.Now.ToUniversalTime() Main End Raise");
while (true) ;
public static Module Connect(string id)
Console.WriteLine("Start Connect");
ChannelFactory<IModuleServer> pipeFactory =
new ChannelFactory<IModuleServer>(
new NetNamedPipeBinding(),
new EndpointAddress($"net.pipe://localhost/id")
);
IModuleServer serverProxy = pipeFactory.CreateChannel();
Module ret = serverProxy.GetModule();
Console.WriteLine("End Connect");
return ret;
以下文件设置主机:
Program
using System;
namespace IPCServer
class Program
static HOST host;
static void Main(string[] args)
host = new HOST("WellKnownName");
Module.Instance.myevent += Instance_myevent;
Console.WriteLine($"DateTime.Now.ToUniversalTime() Server Subscribed to Module.Instance.id");
while (true) ;
private static void Instance_myevent(object sender, EventArgs e)
Console.WriteLine($"DateTime.Now.ToUniversalTime() Server Event Fired from (sender as Module).id");
Module
using System;
using System.Linq;
namespace IPCServer
public class Module
public static Module Instance get; = new Module();
public event EventHandler myevent = delegate ;
public string id;
private Module()
var guid4 = Guid.NewGuid().ToString().Take(4);
id = new String(guid4.ToArray());
Console.WriteLine($"Module Constructor id");
myevent += Module_myevent;
private void Module_myevent(object sender, EventArgs e)
Console.WriteLine($"Module Listener (sender as Module).id");
public void RaiseEvent()
Console.WriteLine($"Module Start Raise id");
myevent(this, EventArgs.Empty);
Console.WriteLine($"Module End Raise id");
Host
using System;
using System.ServiceModel;
namespace IPCServer
internal class HOST
ServiceHost host;
internal HOST(string id)
host = new ServiceHost(typeof(ModuleServer), new Uri[] new Uri("net.pipe://localhost") );
host.AddServiceEndpoint(typeof(IModuleServer), new NetNamedPipeBinding(), id);
host.Open();
Console.WriteLine($"DateTime.Now.ToUniversalTime() Host Opened");
~HOST()
if (host.State == CommunicationState.Opened)
host.Close();
host = null;
Console.WriteLine($"DateTime.Now.ToUniversalTime() Host Destructed");
[ServiceContract]
public interface IModuleServer
[OperationContract]
Module GetModule();
public class ModuleServer : IModuleServer
public Module GetModule()
Console.WriteLine($"DateTime.Now.ToUniversalTime() ModuleServer start GetModule");
Module ret = Module.Instance;
Console.WriteLine($"DateTime.Now.ToUniversalTime() ModuleServer end GetModule");
return ret;
示例运行,这是我系统上的输出:
为什么我没有从服务器进程中获取我的 Singleton。 为什么我的事件没有在服务器中引发。
编辑:服务器打开主机并订阅单例。客户端连接后,它通过成员函数引发事件。事件有两个订阅者,一个在构造函数中,一个在服务器端。仅处理 Module 内部订阅 - 服务器端没有事件处理 - 服务器端没有触发任何事件。模块侦听器被触发,但不在主机进程内。此事件在客户端处理。
【问题讨论】:
哪些事件没有被引发(我看了几次) @BugFinder 我编辑以澄清这一点 - 你是对的,输出列出了处理事件的侦听器。但这不是主机订阅,也不在主机进程内。 【参考方案1】:为什么我没有从服务器进程中获取我的单例
命名管道使用序列化将对象从服务器传递到客户端。这意味着客户端必须重新运行构造函数并复制现有属性。public string id;
是一个字段,因此不会被“复制”,因此会保留构造函数设置的随机值。这就是为什么“相同”对象有不同的 ID。
要解决这个问题,您可以将其更改为:
[DataContract]
public class Module
[DataMember]
public string id get; set;
为什么我的事件没有在服务器中引发。
这不是 WCF 命名管道的工作方式,因为您在客户端只有一个重复版本。我建议你阅读Duplex Channel
【讨论】:
谢谢-怀疑答案可能是我对命名管道完全错误。以上是关于通过命名管道访问单例会创建第二个单例。未在主机端触发的事件的主要内容,如果未能解决你的问题,请参考以下文章