我需要在C#中访问全局(非托管)内存

时间:2019-06-14 06:29:42

标签: c# memory global

我在C#中还很陌生,可能需要一些有关音频项目的帮助。 我的音频输入缓冲区填满时会调用一个方法。在方法中,我将其缓冲到本地float [],并将其传递给函数,在此函数中完成了一些音频处理。处理完该函数后,将操纵的float []返回给我,由Marshall.copy传递给音频输出缓冲区。它可以工作,但是很难以足够快的速度完成音频处理,将结果传回方法而又不会出现难看的故障。如果扩大音频缓冲区,它会变得更好,但在信号链中会出现无法忍受的高延迟。 一个问题是GC。我的DSP例程正在执行一些FFT,这些方法经常需要分配局部变量。我觉得这大大拖延了我的进度。
因此,我需要一种方法来分配一次(并重新访问)一些非托管内存,在整个运行时中保留此内存,并仅从方法中对其进行引用。

我发现例如:

IntPtr hglobal = Marshal.AllocHGlobal(8192);
Marshal.FreeHGlobal(hglobal);

所以我想用一个静态成员定义一个全局静态类“ Globasl”,并将该IntPtr分配给它。

Globals.mem1 = hglobal;

从任何其他方法中,我现在都可以通过 例如

int[] f = new int[2];
            f[0] = 111;
            f[1] = 222;
Marshal.Copy(f, 0, Globals.mem1, 2);

现在是我的问题: 如果要通过其他方法从上面的示例访问此int [],该怎么办?


感谢您的快速帮助。 似乎我有点不准确,对不起

我的音频设备驱动程序引发了一个我捕获的缓冲区已填充事件(由于我现在无法访问自己的家用台式机,所以使用伪代码)。看起来像:

void buffer (....)
    {
    byte[] buf = new byte[asiobuffersize];
    marshall.copy(asioinbuffers, 0, buf, asiobufferlenth);
    buf= manipulate(buf);
    marshall.copy(buf, 0, asiooutbuffer, asiobufferlenth);
    }

操纵函数正在执行从字节到浮点的一些转换,然后再进行一些数学运算(FFT),然后反向转换为字节,就像例如

private byte[] manipulate(byte[] buf, Complex[] filter)
     {
     float bu = convertTofloat(buf); //conversion from byte to audio float here
     Complex[] inbuf = new Complex[bu.Length];
     Complex[] midbuf = new Complex[bu.Length];
     Complex[] mid2buf = new Complex[bu.Length];
     Complex[] outbuf = new Complex[bu.Length];
     for ( n....)
     {
     inbuf[n]= bu[n];    //Copy to Complex
     }

     midbuf=FFT(inbuf);         //Doing FFT transform

     for (n....)
     {
     mid2buf[n]=midbuf[n]*filter[n];    //  Multiply with filter
     } 

     outbuf=iFFT(mid2buf)        //inverse iFFT transform

     byte bu = convertTobyte(float); //conversion from float back to audio byte

     return bu;
     }

我希望在这里出现速度问题。因此,我认为,如果操作功能可以“获得”固定的非托管内存,并且该内存(一旦创建)就固定了所有这些变量(例如,Complex)并预先分配了内存,则可以解决该问题,因此我一定不能每次调用函数时创建一个新的。我首先预计到错误的FFT或数学错误的原因,但它发生在几秒钟的“尖锐”时间距离内,因此它与诸如削波之类的音频信号问题无关。我认为问题发生了,当GC进行了一些认真的工作,并且恰好让我吃掉了这几毫秒的时间,以便及时填补缓冲区的不足时。 可以接受。

1 个答案:

答案 0 :(得分:0)

我真的怀疑您遇到的问题是由托管缓冲区创建/复制引起的。相反,我认为您的问题是您的数据捕获逻辑与DSP逻辑结合在一起。通常,捕获的数据驻留在循环缓冲区中,该数据在一段时间后会被重写,因此您应该在可能的情况下尽快获取此数据。 问题在于,直到完成DSP后,您才获取下一个可用的数据块。您已经知道FFT操作确实占用大量CPU!如果您有处理高峰,则可能无法在捕获驱动程序重写数据之前检索数据。

解决您问题的一种可能性是,尝试增加捕获缓冲区的大小和/或可用数量(如果可能)。这样可以为您腾出更多时间来重写捕获的数据。另一种可能性,也是我最喜欢的一种可能性,是将处理阶段与捕获阶段分离,这样,如果在忙于执行DSP计算时有新数据可用,您仍然可以并在它可用时几乎对其进行缓冲。您将更容易适应manipulate方法内部由垃圾收集引起的暂停或计算峰值。

这将涉及创建两个线程:捕获线程和处理线程。您还需要一个“事件”来通知处理线程新数据可用,以及一个队列,它将用作动态可扩展缓冲区。

捕获线程如下所示:

// the following are class members
AutoResetEvent _bufQueueEvent = new AutoResetEvent(false);
Queue<byte[]> _bufQueue = new Queue<byte[]>;
bool _keepCaptureThreadAlive;
Thread _captureThread;

void CaptureLoop() {
    while( _keepCaptureThreadAlive ) {

       byte[] asioinbuffers = WaitForBuffer();  
       byte[] buf = new byte[asiobuffers.Length]
       marshall.copy(asioinbuffers, 0, buf, asiobufferlenth);
       lock( _bufQueue ) {
          _bufQueue.Enqueue(buf);
       }
       _bufQueueEvent.Set();   // notify processing thread new data is available
    }
}

void StartCaptureThread() {
   _keepCaptureThreadAlive = true;
   _captureThread = new Thread(new(ThreadStart(CaptureLoop));
   _captureThread.Name = "CaptureThread";
   _captureThread.IsBackground = true;
   _captureThread.Start();
}

void StopCaptureThread() {
   _keepCaptureThreadAlive = false;
   _captureThread.Join();  // wait until the thread exits.
}

处理线程看起来像这样

// the following are class members
bool _keepProcessingThreadAlive;
Thread _processingThread;

void ProcessingLoop() {
   while( _keepProcessingThreadAlive ) {
      _bufQueueEvent.WaitOne();  // thread will sleep until fresh data is available
      if( !_keepProcessingThreadAlive ) {
         break;  // check if thread is being waken for termination.
      }   
      int queueCount;
      lock( _bufQueue ) {
        queueCount = _bufQueue.Count;
      }
      for( int i = 0; i < queueCount; i++ ) {
         byte[] buffIn;
         lock( _bufQueue ) {
            // only lock during dequeue operation, this way the capture thread Will
            // be able to enqueue fresh data even if we are still doing DSP processing
            buffIn = _bufQueue.Dequeue();
         }
         byte[] buffOut = manipulate(buffIn);  // you are safe if this stage takes more time than normal, you will still get the incoming data   
         // additional logic using manipulate() return value
         ...
      }
   }
}

void StartProcessingThread() {
  _keepProcessingThreadAlive = true;
  _processingThread = new Thread(new(ThreadStart(ProcessingLoop));
  _processingThread.Name = "ProcessingThread";
  _processingThread.IsBackground = true;
  _processingThread.Start();
}

void StopProcessingThread() {
   _keepProcessingThreadAlive = false;
   _bufQueueEvent.Set();  // wake up thread in case it is waiting for data
   _processingThread.Join();
}

在我的工作中,我们还执行许多DSP,这种模式确实帮助我们解决了您遇到的问题。