C# 使用SharedMemory MMF实现快速的.NET无锁内部进程

C# 使用SharedMemory MMF实现快速的.NET无锁内部进程,c#,multithreading,performance,ipc,wait-free,C#,Multithreading,Performance,Ipc,Wait Free,我不熟悉多任务处理和IPC,我正在尝试使用共享内存构建一种快速进程间通信的方法(起初我研究IPC术语,考虑到wcfsockets和named pipes,最终发现了MMF) 现在,我已经通过使用Lock和EventWaitHandle信令在两个进程之间使用共享内存成功地实现了一个小测试,现在我开始使用一种实现非阻塞/无等待模式的方法。现在,我正在尝试组合Thread.MemoryBarrier()&从MemoryMapedFile读取信令扇区 问题还不清楚!第一轮通过,第二轮最后一站在百慕大三角

我不熟悉多任务处理和IPC,我正在尝试使用共享内存构建一种快速进程间通信的方法(起初我研究IPC术语,考虑到
wcf
sockets
named pipes
,最终发现了MMF)

现在,我已经通过使用
Lock
EventWaitHandle
信令在两个进程之间使用共享内存成功地实现了一个小测试,现在我开始使用一种实现非阻塞/无等待模式的方法。现在,我正在尝试组合
Thread.MemoryBarrier()
&从
MemoryMapedFile
读取
信令扇区

问题还不清楚!第一轮通过,第二轮最后一站在百慕大三角。。。超出调试器的范围

假设进程a正在向进程b发送对
showMsg()
的突发请求

                                         //offset positions in mmf
MemoryMappedViewAccessor MmfAcc; const int opReady= 0, opCompleteRead = 4, .....



ReadTrd()
{
  //[0,3] - Reader is stationed
  //[4,7] - Read Complete successfully
  //[8,11] - Data-size 
  //[12,15] - Reader-exiting
  "format" the signals Section (write zeroes). 

   for(;;){if (WrTrd-StepMMF1  Confimed) break;}
  MmfAcc- read DataSize val @offset[8]
  MmfAcc- read Data val @offset[50]
  MmfAcc Write exit to offset....
  ....heavy use of  Thread.MemoryBarrier(); sets !!! (all over the place, on every shared variable...)

}

writeTrd()
{
  heavy use of  Thread.MemoryBarrier() !!!
  //[15-19] - wr is stationed
  //[20-23] - wr Complete successfully
  //[24-27] - wrExiting
  "format" the signals Section . 
   for(;;){if Reader-StepMMF1 is Confim break;}
   MmfAcc- DataSize to offset[8]
   write Data To offset[50] using the method below
   for(;;){if Read StepMMF2 is Confim break;}
 } 
当我第一次使用命名管道解决方案时,与命名管道方法相比,Mmf方法(尽管有
Lock
和EventWaitHandle)有很大的性能提升,但我是否可以进一步使用上述方法呢

我可以像条带化Raid一样复制这个模式

Reader1 + Reader2 & WriteThred1  + WriteThread2
所以我试过了,在那一点上卡住了

这种使用完整memoryfence&sharedmemory发送信号的方法有效吗

如果是这样,剩下的就是看第二次迭代失败的原因,性能差异

编辑-在额外线程测试后面添加了逻辑

这就是我用来操纵writer线程的“桥”(对读者来说也是这样)

public void Write(byte[] parCurData)
{
    if (ReadPosition < 0 || WritePosition < 0)
        throw new ArgumentException();
    this.statusSet.Add("ReadWrite:-> " + ReadPosition + "-" + WritePosition);
    // var s = (FsMomitorIPCCrier)data;

    ////////lock (this.dataToSend)
    ////////{
    Thread.MemoryBarrier();
        LiveDataCount_CurIndex = dataQue.Where(i => i != null).Count();
    this.dataQue[LiveDataCount_CurIndex] = parCurData;

    Console.WriteLine("^^^^^" + Thread.CurrentThread.Name + " has Entered WritingThreads BRIDGE");
    Console.WriteLine("^^^^^[transactionsQue] = {1}{0}^^^^^[dataQue.LiveDataASIndex = {2}{0}^^^^^[Current Requests Count = {3}{0}", "\r\n", Wtransactions, LiveDataCount_CurIndex, ++dataDelReqCount);

    //this.itsTimeForWTrd2 = false;

    if (Wtransactions != 0 && Wtransactions > ThrededSafeQ_Initial_Capcity - 1)
    if (this.dataQueISFluded) this.DataQXpand();


    if (itsTimeForWTrd2)
    {

        bool firstWt = true;
        while (writerThread2Running)
        {
            if (!firstWt) continue;
            Console.WriteLine("SECOND WRITERThread [2] is In The CoffeeCorner");
                    firstWt=false;
        }

        this.dataDelivery2 = this.dataQue[LiveDataCount_CurIndex];
        Console.WriteLine("Activating SECOND WRITERThread [2]");
        itsTimeForWTrd2 = false;

        writerThread2Running = true;
        //writerThread1Running = true;
        writerThread2 = new System.Threading.Thread(WriterThread2);
        writerThread2.IsBackground = true;
        writerThread2.Name = this.DepoThreadName + "=[WRITER2]";
        writerThread2.Start();

    }
    else
    {
        bool firstWt = true;
        while (writerThread1Running)
        {
            if (!firstWt)continue;
                Console.WriteLine("WRITERThread [1] is In The CoffeeCorner");
            firstWt=false;
        }
        Console.WriteLine("Activating WRITERThread [1]");
        this.dataDelivery1 = this.dataQue[LiveDataCount_CurIndex]; 

        writerThread1Running = true;
        writerThread1 = new System.Threading.Thread(WriterThread1);
        writerThread1.IsBackground = true;
        writerThread1.Name = this.DepoThreadName+"=[WRITER1]";
        writerThread1.Start();
        itsTimeForWTrd2 = true;

    }
    Thread.MemoryBarrier();
}

正如我所说的,我已经研究了通过非阻塞/无等待等方式同步数据和共享内存的问题。信号量锁定,因此我试图消除在将数据传输到共享内存映射文件的每个事务过程中的任何开销。我在这里问消除锁定和事件会有什么问题WaitHandle并将其替换为内存围栏逻辑和通过mmf发送信号?

如果您计划将其用于研发以外的特定用途,最简单的方法是使用已经提供了该功能的库。考虑这一点的一种方法是,无锁=消息传递。两种可能的方法是:使用ISIC消息传递实现ZeroMQ IPC,它提供了强大的.Net支持和卓越的IPC性能(),要获得更完整的参与者模型实现(包括对IPC的支持),请查看Akka.Net()


如果目的是更多的研发(也就是说,你希望编写自己的实现,这很酷),我仍然建议看看这些产品的来源(特别是Akka.net,因为它是用C#编写的),了解有关消息传递和基于角色的IPC的实现思想。

我真的不明白为什么它在一分钟内获得了三张赞成票(很可能是因为“噢,酷,内存映射文件”);你能不能花点功夫把句子大写并加上标点符号,试着一步一步地解释这段代码应该做什么,以及它在哪里停止了你所期望的工作?我不完全确定,但我认为展示一些实际代码而不是伪代码可能也会有所帮助。没问题,我认为你已经有了相当多的经验你手上有一个有趣的问题,我知道一些关于线程和内存映射文件的知识,只是很难找出真正的问题。仅此而已。这完全是错误的,你根本不需要锁。你真正需要的是一个事件,它告诉你另一个线程已经完成了数据的写入o您可以可靠地读取它。一个可等待事件,这样您就不会在不必要地等待另一个线程完成任务时烧掉内核。在.NET中随时可用,例如,使用命名的AutoResetEvent。在写入的线程中调用它的Set()方法,它的WaitOne()线程中读取的方法。在没有种族的情况下使其完全可靠将使您忙碌一段时间,很有可能您会重新发明一个命名管道。@HansPassant这是一些关于所有开销的图表过早优化是根源。。。
public unsafe byte[] UsReadBytes(int offset, int num)
{
    byte[] arr = new byte[num];
    byte* ptr = (byte*)0;
    this.accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
    Marshal.Copy(IntPtr.Add(new IntPtr(ptr), offset), arr, 0, num);
    this.accessor.SafeMemoryMappedViewHandle.ReleasePointer();
    return arr;
}