在线程之间的Invoke()上检测到CallbackOnCollectedDelegate

时间:2012-10-24 12:09:14

标签: c# multithreading delegates invoke

我已经阅读过类似问题的解决方案,但是我无法让他们在我的案例中工作。我在Visual Studio 2010中运行的C#.NET 4.0项目中收到以下错误:

  

检测到CallbackOnCollectedDelegate消息:进行了回调   在垃圾收集的类型委托上   'VLCMTest!VLCMTest.Data + VCECLB_GrabFrame_CallbackEx ::调用'。这可能   导致应用程序崩溃,损坏和数据丢失。路过时   委托非托管代码,他们必须由托管保持活着   应用程序,直到确保它们永远不会被调用。

以下是我的情况:我有一个后台线程,在收集数据帧时会收到通知。

protected AutoResetEvent frameGrabbed;
public event EventHandler<DataFrameInfo> FrameGrabbedEvent;

    private void DataCollectionThread()
    {
        while (true)
        {
            frameGrabbed.WaitOne();

            lock (locker)
            {
                FrameGrabbedEvent(this, new DataFrameInfo(lastFrame.BufferIndex, lastFrame.FrameNumber, lastFrame.FrameTimestamp));
            }
        }
    }

DataFrameInfo类存储有关帧的一些信息(索引到帧缓冲区,帧编号和时间戳)。我创建了这个类的一个实例并将其传递给主线程,以便显示数据。主线程中的代码如下所示:

    // Delegate for the Invoke call, 
    // make it static to prevent a problem with garbage collection
    delegate void GetFrameDelegate(DataFrameInfo frameInfo);
    private static GetFrameDelegate d;

    /// <summary>
    /// Did we just receive a frame?
    /// </summary>
    /// <param name="source"></param>
    /// <param name="args"></param>
    void frameGrabbed(object source, DataFrameInfo args)
    {
        if (this.InvokeRequired)
        {
            // It's on a different thread, so use Invoke.
            d = new GetFrameDelegate(GetFrame);
            this.Invoke(d, new object[] { args });
            return;
        }

        // Get the Frame
        GetFrame(args);
    }

    private void GetFrame(DataFrameInfo frameInfo)
    {
        // Call Display Frame
        Debug.WriteLine("Frame: The bufferIndex is " + frameInfo.BufferIndex);
        Debug.WriteLine("Frame: The number is " + frameInfo.FrameNumber);
        Debug.WriteLine("Frame: The timestamp is " + frameInfo.FrameTimestamp / 1000);
    }

FrameGrabbedEvent连接到frameGrabbed()函数。由于从另一个线程调用frameGrabbed(),因此应该需要Invoke。那么现在,我只是在我开始显示数据之前尝试转储帧细节。

有趣的是,该计划将运行一段时间。只要我在桌面上移动程序的主窗口,错误就会立即出现。我必须改变时间,以便在使用对象之前收集垃圾。似乎最常建议的解决方案是让代表保持静态,但这对我不起作用。

更新

听起来我错过了一些相关的代码。下面是导致frameGrabbed事件的代码。本质上它是一个由我正在使用的DLL调用的中断处理程序。

我声明如下:

// Function pointer used by StartGrabEx
public delegate void GrabFrame_CallbackEx(IntPtr userData, ref FrameInfoEx frameInfo);

然后我用:

开始数据收集
    public void Start()
    {

        // Start grabbing frames
        isGrabRunning = true;
        GrabFrame_CallbackEx callback = new GrabFrame_CallbackEx(GrabCallback);
        StartGrabEx(callback);
    }

GrabCallback函数如下所示:

    FrameInfo lastFrame;

    private void GrabCallback(IntPtr userData, ref FrameInfoEx frameInfo)
    {
        // Are we grabbing frames?
        if (!isGrabRunning)
        {
            return;
        }

        lock (locker)
        {
            lastFrame = new DataFrameInfo(bufferIndex, frameInfo.number, frameInfo.timestamp);
        }

        // We've captured a frame, notify the DataCollectionThread
        frameGrabbed.Set();
    }

查看此更新,可能问题出在lastFrame上,虽然我认为我更改了代码以更新GrabCallback中的lastFrame(而不是使用new)并且仍然失败。

更新#2

也许我还应该提到DataCollectionThread声明为:

DataCollectionThread = new Thread(DataCollectionThread );
DataCollectionThread .Name = "DataCollectionThread ";
DataCollectionThread .IsBackground = true;
DataCollectionThread .Start();

1 个答案:

答案 0 :(得分:1)

正如Hans Passant所说,问题在于Start()方法中的回调变量。我将其更改为以下内容:

private GrabFrame_CallbackEx callback;

public void Start()
{

    // Start grabbing frames
    isGrabRunning = true;
    callback = new GrabFrame_CallbackEx(GrabCallback);
    StartGrabEx(callback);
}

现在一切正常!