環境: .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()
都沒有。如果您釋放內存,您最好希望GC.Collect()執行一些有意義的操作。這兩種方法都沒有。如果它需要去任何地方,很不清楚爲什麼,那麼它應該在close()方法中。將* byteArray *設置爲null後。這段代碼很危險,它很容易忘記調用close()和嚴重泄漏內存。這解釋了爲什麼你認爲你需要編寫這些代碼。它不會解決這個問題,一次性模式是必需的。 –
目前還不清楚你想要做什麼。然而,這些代碼在許多方面都有不可迴避的缺陷,唯一真正的選擇是創建一個新的問題,明確說明目標並重新開始。 –