2014-02-06 76 views
0

環境: .NET4.0,基於Winform的應用程序,Windows 7(x64),VS2010。使用GC句柄通過GC.Collect()鎖定對象

這裏是測試類的代碼片段,它使用GC句柄用GC.Collet()固定對象。我有2個方法,如「Method1」和「Method2」。我想知道使用GC.Collect()時使用GC句柄來固定對象的正確方法。

代碼段:

class Test() 
{ 
      [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] 
      public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); 

      private GCHandle pinnedArray; 
      public GCHandle PinnedArray 
      { 
       get { lock (this) { return pinnedArray; } } 
       set { lock (this) { pinnedArray = value; } } 
      } 

      IntPtr pointer; 
      public IntPtr Pointer 
      { 
       get { lock (this) { return pointer; } } 
       set { lock (this) { pointer = value; } } 
      } 

      public instance() 
      { 
       const int size = 1024 * 768; 
       byte[] byteArray = null; 
       byteArray = new byte[size]; 

       GCHandle PinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned); 
       IntPtr Pointer = PinnedArray.AddrOfPinnedObject(); 


      } 

      public void Method1(ref IntPtr ImageBuffer) 
      { 
       try 
       { 
       unsafe 
       { 
        if (ImageBuffer != IntPtr.Zero) 
        { 
         if (Pointer != null) 
         { 
          CopyMemory(Pointer, ImageBuffer, ImageSize); 

          // do stuff 
          // 

          m_numAcqs++; 
          if (m_numAcqs > _numMaxAcqs) 
          { 
           GC.Collect(); 
           m_numAcqs = 0; 
          } 
         } 
        }        
       } 
       } 
       catch (Exception ex) 
       { 
        log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace)); 
       } 

       return; 
      } 

      public void Method2(ref IntPtr ImageBuffer) 
      { 
       try 
       { 
       unsafe 
       { 
        if (ImageBuffer != IntPtr.Zero) 
        { 
         if (Pointer != null) 
         { 
          CopyMemory(Pointer, ImageBuffer, ImageSize); 

          // do stuff 
          // 
         } 
        }        
       } 

       m_numAcqs++; 
       if (m_numAcqs > _numMaxAcqs) 
       { 
        GC.Collect(); 
        m_numAcqs = 0; 
       } 
       } 
       catch (Exception ex) 
       { 
        log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace)); 
       } 

       return; 
      } 


      public close() 
      { 
       PinnedArray.free(); 
      } 
    } 

EDITED問題: 對不起,我不清楚的問題。我爲我的問題附上真實的代碼。我有System.AccessViolationException錯誤。我有用於處理新圖像的回調方法。這是代碼片段。建議使用Marshal.Copy方法。但是,如果我使用這種方法,則系統非常慢。

// Local callback function used for handle new images 
     void HandleImage(ref Jai_FactoryWrapper.ImageInfo ImageInfo) 
     { 
     // Jai_FactoryWrapper.EFactoryError error = Jai_FactoryWrapper.EFactoryError.Success; 

     // This is in fact a callback, so we would need to handle the data as fast as possible and the frame buffer 
     // we get as a parameter will be recycled when we terminate. 
     // This leaves us with two choises: 
     // 1) Get the work we need to do done ASAP and return 
     // 2) Make a copy of the image data and process this afterwards 
     // 
     // We have the access to the buffer directly via the ImageInfo.ImageBuffer variable 
     // 
     // We can access the raw frame buffer bytes if we use "unsafe" code and pointer 
     // To do this we need to set the "Allow unsafe code" in the project properties and then access the data like: 
     // 
     // unsafe 
     // { 
     //  // Cast IntPtr to pointer to byte 
     //  byte* pArray = (byte*)ImageInfo.ImageBuffer; 
     //  // Do something with the data 
     //  // Read values 
     //  byte value = pArray[10]; 
     //  // Write values 
     //  for (int i = 0; i < 1000; i++) 
     //   pArray[i] = (byte)(i % 255); 
     // } 

     // If we want to copy the data instead we can do like this without Unsafe code: 
     byte[] array = null; 

     if (ImageInfo.ImageBuffer != IntPtr.Zero) 
     { 
      // Allocate byte array that can contain the copy of data 
      array = new byte[ImageInfo.ImageSize]; 
      IntPtr memoryDest = Marshal.AllocHGlobal((int)ImageInfo.ImageSize); 

      // Do the copying 
      Marshal.Copy(ImageInfo.ImageBuffer, array, 0, (int)ImageInfo.ImageSize); 
      Marshal.Copy(array, 0, memoryDest , (int)ImageInfo.ImageSize); 

      // Do something with the raw data 
      CopyToCogBuffer(memoryDest); 
     } 

     return; 
     } 

private void CopyToCogBuffer(IntPtr pointer) 
{ 
    try 
    { 
     ICogImage8RootBuffer CogBuffer = new CogImage8Root(); 
     CogBuffer.Initialize(int.Parse(myWidthNode.Value.ToString()), int.Parse(myHeightNode.Value.ToString()), pointer, int.Parse(myWidthNode.Value.ToString()), null); 
     if (CogBuffer != null) 
     { 
      CogImage8Grey pImage1 = new CogImage8Grey(); 
      CogImage8Grey pImage2 = new CogImage8Grey(); 

      pImage1.SetRoot(CogBuffer); 
      pImage2 = pImage1.Copy(CogImageCopyModeConstants.CopyPixels); 
      PImage = pImage2; 

      _numLiveDisplay++; 
      if (_numLiveDisplay > _numMaxLiveDisplay) 
      { 
       if (_liveProcessing) 
       { 
        if (CogDisplay != null) 
         CogDisplay.Image = (CogImage8Grey)pImage2; 

        m_numAcqs++; 
        if (m_numAcqs > _numMaxAcqs) 
        { 
         GC.Collect(); 
         m_numAcqs = 0; 
        } 
       } 

       _numLiveDisplay = 0; 
      } 
     } 
    } 
    catch (CogException ex) 
    { 
     log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace)); 
    } 
    catch (Exception ex) 
    { 
     log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace)); 
    } 
} 

因此我已經更改爲固定對象。如果使用Kernel32.dll的「CopyMemory」方法,速度非常快。但是,隨機發生「System.AccessViolationException」。我想知道GCHandle PinnedArray是否正確使用或不正確。

private void HandleImage(ref Jai_FactoryWrapper.ImageInfo ImageInfo) 
     { 
      try 
      { 
       if (recipe.CameraInfo[indexID].Use) 
       { 
        byte[] byteArray = null; 
        if (ImageInfo.ImageBuffer != IntPtr.Zero) 
        { 
         // Allocate byte array that can contain the copy of data 
         byteArray = new byte[ImageInfo.ImageSize]; 
         GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned); 
         IntPtr pointer = pinnedArray.AddrOfPinnedObject(); 
         CopyMemory(pointer, ImageInfo.ImageBuffer, ImageInfo.ImageSize); 
         CopyToCogBuffer(pointer); 
         pinnedArray.Free(); 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace)); 
      } 

      return; 
     } 

這是來自EventViewer的調用堆棧信息。

Application Program : ImageDelegateSample.exe 
Framework Version: v4.0.30319 
Description: The process was terminated due to an unhandled exception. 
Exception Info: System.AccessViolationException 
Stack : 
at: Cognex.VisionPro.CogImage8Grey.Copy(Cognex.VisionPro.CogImageCopyModeConstants) 
at: ImageDelegateSample.ClientManager.CopyToCogBuffer(IntPtr) 
at: ImageDelegateSample.ClientManager.HandleImage(ImageInfo ByRef) 
at: Jai_FactoryDotNET.CCamera.HandleImage(ImageInfo ByRef) 
at: Jai_FactoryDotNET.CCamera+StreamWork.StreamThread() 
at: System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 
at: System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) 
at: System.Threading.ThreadHelper.ThreadStart() 
+0

都沒有。如果您釋放內存,您最好希望GC.Collect()執行一些有意義的操作。這兩種方法都沒有。如果它需要去任何地方,很不清楚爲什麼,那麼它應該在close()方法中。將* byteArray *設置爲null後。這段代碼很危險,它很容易忘記調用close()和嚴重泄漏內存。這解釋了爲什麼你認爲你需要編寫這些代碼。它不會解決這個問題,一次性模式是必需的。 –

+0

目前還不清楚你想要做什麼。然而,這些代碼在許多方面都有不可迴避的缺陷,唯一真正的選擇是創建一個新的問題,明確說明目標並重新開始。 –

回答

4

也沒有。你最好希望GC.Collect()能夠發揮版本的內存。這兩種方法都沒有。如果它需要去任何地方,很不清楚爲什麼,那麼它應該在close()方法中。將byteArray設置爲空之後。

這段代碼很危險,它很容易忘記調用close()和嚴重泄漏內存。這解釋了爲什麼你認爲你需要編寫這些代碼。它不會解決這個問題,一次性模式是必需的。終結器可以保證您不會忘記取消固定數組,並且Dispose()方法可以幫助您使用聲明成功進入的陷阱。

CopyMemory()調用也非常危險,沒有檢查ImageSize < = size並且不檢查圖像格式。 1024 * 768不足以存儲1024 x 768位圖,像素通常需要3或4個字節。堆損壞非常難以調試。始終支持Marshal.Copy(),它不會允許損壞GC堆。具有額外的優點,它不需要固定陣列。

+0

謝謝,如果我使用Marshal.Copy,那麼Speed非常慢,所以我使用CopyMemory和PinnedArray。你有評論如果我使用正確與否? –

+0

如果您看到Marshal.Copy比CopyMemory慢,那麼您沒有正確使用它。當然不知道爲什麼。 –