假设我的大数据阵列每秒更新1000次以上 另一个应用程序想要在短时间内访问和读取数组。两个应用程序都在同一台机器上。
我尝试过使用WCF进行进程间通信,但是每秒数千次序列化和发送整个数组(或大型对象)是不可行的。 有没有办法在c#中直接访问来自不同应用程序的对象?
答案 0 :(得分:26)
虽然早期的WCF今天仍然有用,但您可以使用一些IPC技术。
Pipes 就是这样一种技术。它是二进制的,在内核模式下运行非常快!虽然它的级别很低,但不可以访问“对象”。
.NET Remoting 将提供对象的访问权限,但可能没有管道那么快。
管道和.NET远程处理比序列化技术WCF更快,WCF将事物转换为详细的XML / SOAP。
COM是IPC的二进制协议。 COM是客户端服务器模型,客户端从COM或OLE服务器请求数据。关于COM的美妙之处在于,您对服务器中的对象进行了直接访问 - 它们不是序列化的。例如,请求SAFEARRAY中的元素。
SAFEARRAY
是由类型安全数据组成的任意维度的自动化安全结构。幸运的是,.NET会为我们隐藏SAFEARRAY狼吞虎咽。
在我的示例中,我创建了一个将公开数组的Manager
类。为了到达Manager
我使用了工厂模式,以便Manager
基本上是一个单身人士。
您应按如下方式布置项目:
Factory
,Manager
首先是合同:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IArrayItem
{
#region Properties
string Name { get; set; }
int Whatsit { get; set; }
#endregion
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IFactory
{
#region Methods
IManager CreateManager();
#endregion
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IManager
{
#region Properties
IArrayItem[] Array { get; }
#endregion
}
public static class MyComLibConstants
{
public const string FactoryProgId = "MickyD.MyComLib.Factory.1";
}
现在为工厂模式:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof (IFactory))]
[Guid("...")]
[ProgId(MyComLibConstants.FactoryProgId)]
public class Factory : MarshalByRefObject, IFactory
{
#region IFactory Members
/// <summary>
/// Creates the manager.
/// </summary>
/// <returns></returns>
public IManager CreateManager()
{
return Manager.Instance;
}
#endregion
}
经理:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof (IManager))]
[Guid("...")]
internal sealed class Manager : MarshalByRefObject, IManager
{
private static Manager _instance;
#region Constructor
/// <summary>
/// Prevents a default instance of the <see cref="Manager"/> class from being created.
/// </summary>
private Manager()
{
const int n = 5000;
Array = new IArrayItem[n];
for (int i = 0; i < n; i++)
{
Array[i]=new ArrayItem();
}
}
#endregion
#region Properties
/// <summary>
/// Gets the instance.
/// </summary>
/// <value>
/// The instance.
/// </value>
public static IManager Instance
{
get
{
if (_instance == null)
{
_instance = new Manager();
}
return _instance;
}
}
#endregion
#region IManager Members
/// <summary>
/// Gets the array.
/// </summary>
/// <value>
/// The array.
/// </value>
public IArrayItem[] Array { get; private set; }
#endregion
}
测试应用。这应该仅引用 MyComLib.Contracts.dll 和而不是MyComLib.dll 。
class Program
{
static void Main(string[] args)
{
var type = Type.GetTypeFromProgID(MyComLibConstants.FactoryProgId);
var factory = Activator.CreateInstance(type) as IFactory;
var manager = factory.CreateManager();
var x = manager.Array[500].Whasit;
}
}
最后一步是将此进程内 COM服务器更改为进程外 COM服务器,以便多个进程共享相同的{{1}并且不要创建自己的单身人士。换句话说,跨越进程的单身人士。当Manager
正在运行时,它基本上位于与所有其他客户端进程分开的自己的进程空间中。
为此,您需要配置一个COM代理,详细解释here。
最后,文件映射允许您操作文件,就好像它只是进程地址空间中的大块内存一样。没有繁琐的文件寻求;读/写操作。只需抓住指向内存块的指针即可开始读/写。系统将完成其余的工作。
MSDN:
您可以使用特殊的文件映射案例在进程之间提供命名共享内存。如果在创建文件映射对象时指定系统交换文件,则将文件映射对象视为共享内存块。其他进程可以通过打开相同的文件映射对象来访问同一块内存。 Tell me more
可悲的是,它仍然需要您首先编写数据并且为了最有效,您需要更改应用程序以将内存块视为事实的来源,而不是内存中的数组。否则你将一直在序列化。
但是,通过交换文件共享内存在技术上允许您消除客户端 - 服务器应用程序与“堆上”数据重复之间的任何序列化反序列化。虽然正如我所说,您可能需要调整您的应用程序以使用原始内存缓冲区而不是对象。
注意:与流行的看法相反,.NET Remoting 并非完全过时。一个通用的用途是在同一进程中不同Manager
内的对象之间进行通信,这通常是AppDomains
中的对象。