调用Dispose()后程序挂起

5

我正在开发一个使用C#的iOS项目。该程序从连接的网络摄像头中捕获图像,并通过Socket将其发送到iPhone/iPad。这一切都运行良好,我可以成功地在设备上获取我的流。

但是当客户端断开连接时,网络摄像头必须关闭,在此功能中,程序就会卡住。没有错误消息和异常调用...只是卡住了!我认为这是多线程问题,但不幸的是我在C#方面经验不足,无法找到解决方案。我希望这里有人能把我引到正确的轨道上...

代码:
onImageCaptured函数:

public void OnImageCaptured(Touchless.Vision.Contracts.IFrameSource frameSource, Touchless.Vision.Contracts.Frame frame, double fps)
{
    _latestFrame = frame.Image;
    Console.WriteLine("OnImageCaptured");
    if (isConnected)
    {
        Console.WriteLine("OnImageCaptured - isConnected");
        byteArray = new byte[0];
        MemoryStream stream = new MemoryStream();

        _latestFrame.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
        stream.Close();
        byteArray = stream.ToArray();

        if (byteArray.Length > 0)
        {
            string eof = "<EOF>";
            byte[] eofByte = Encoding.ASCII.GetBytes(eof);
            Console.WriteLine("OnImageCaptured - sendStream");
            this.onDataSend(byteArray);
            this.onDataSend(eofByte);
            stream.Flush();
        }

        System.Diagnostics.Debugger.Log(0, "1", "\nByte Array Length: " + byteArray.Length.ToString());
    }
    pictureBoxDisplay.Invalidate();
}

在相机类中定义如下:

public event EventHandler<CameraEventArgs> OnImageCaptured;

并触发:

OnImageCaptured.Invoke(this, new CameraEventArgs(bitmap, fps));

我相信这个函数是在单独的线程中运行的,因为当图像加载时UI不会被阻塞。

接下来,客户端断开连接是在此函数中处理的:

public void onDataSend(byte[] data)
{
    clientReady = false;
    try
    {
        socketWorker.Send(data);
    }
    catch (SocketException se)
    {
        isConnected = false;
        Console.WriteLine("Error: Data Write - SocketException");
        Console.WriteLine(se.ErrorCode.ToString());
        thrashOldCamera() // THIS FUNCTION HANGS THE PROGRAM !!
        onDisconnectServer();

        // onDisconnectServer();
    }
    catch (ObjectDisposedException)
    {
        isConnected = false;
        Console.WriteLine("Error: Data Write - ObjectDisposedException");
        // onDisconnectServer();
    }

}

客户端断开连接,调用thrashOldCamera()函数。到目前为止都正常!现在:
private void thrashOldCamera()
{
    Console.WriteLine("TrashOldCamera");
    // Trash the old camera
    if (_frameSource != null)
    {
        try
        {
            _frameSource.NewFrame -= OnImageCaptured;
            Console.WriteLine("TrashOldCamera - 1");
            _frameSource.Camera.Dispose(); // HERE IT HANGS. IT NEVER GOES PAST HERE !!!
            Console.WriteLine("TrashOldCamera - 2");
            setFrameSource(null);
            Console.WriteLine("TrashOldCamera - 3");
            pictureBoxDisplay.Paint -= new PaintEventHandler(drawLatestImage);
        }
        catch (Exception ex)
        {
            Console.WriteLine("End Trash Camera Ex: " + ex);
        }
    }
    Console.WriteLine("End Trash Camera");
}

程序在_frameSource.Camera.Dispose();处停止响应。如上所述,没有错误或异常。

onDataReceive()可能在onImageCapture()函数内被调用,这可能是一个问题。

我还向表单添加了一个按钮来触发thrashOldCamera(),这个功能完美地运行。

非常感谢您的帮助和提示。


http://channel9.msdn.com/Series/-NET-Debugging-Stater-Kit-for-the-Production-Environment/Diagnosing-Application-Issues-01 - Lex Li
4个回答

9
这被称为死锁,是一个典型的线程问题。在代码片段中没有看到您明确调用UI线程的任何位置,因此死锁可能位于相机固件本身。核心问题是您尝试在回调仍在执行的同时关闭相机,没有很多代码能够抵御这种情况。Release()调用无法完成直到回调完成,但回调无法完成直到Release()调用完成。死锁城市。
您需要重构代码以防止出现这种情况。延迟释放相机是关键,最好在打开相机的同一线程上完成。例如,在FormClosed事件中释放它可能会解决问题。或者甚至不释放它,让Windows自动关闭句柄。

3

我不确定这应该是评论还是答案,但至少我可以帮助你找到正确的方向。

找到了你正在使用的库的源代码。下面是Camera.Dispose()的内容。

public void Dispose()
{
    StopCapture();
}

好的,那里并没有太多帮助,这是 Camera.StopCapture()

internal void StopCapture()
{
    _cameraMethods.StopCamera();
}

再次说明,帮助不是很大。 _cameraMethodsCameraMethods 的一种类型,它来自于库 WebCamLib,这是一个用于与 direct show 通信的 C++ 辅助库。
这里是 CameraMethods::StopCamera()
void CameraMethods::StopCamera()
{
    if (g_pMediaControl != NULL)
    {
        g_pMediaControl->Stop();
        g_pMediaControl->Release();
        g_pMediaControl = NULL;
    }

    g_pfnCaptureCallback = NULL;

    if (g_pIBaseFilterNullRenderer != NULL)
    {
        g_pIBaseFilterNullRenderer->Release();
        g_pIBaseFilterNullRenderer = NULL;
    }

    if (g_pIBaseFilterSampleGrabber != NULL)
    {
        g_pIBaseFilterSampleGrabber->Release();
        g_pIBaseFilterSampleGrabber = NULL;
    }

    if (g_pIBaseFilterCam != NULL)
    {
        g_pIBaseFilterCam->Release();
        g_pIBaseFilterCam = NULL;
    }

    if (g_pGraphBuilder != NULL)
    {
        g_pGraphBuilder->Release();
        g_pGraphBuilder = NULL;
    }

    if (g_pCaptureGraphBuilder != NULL)
    {
        g_pCaptureGraphBuilder->Release();
        g_pCaptureGraphBuilder = NULL;
    }

    this->activeCameraIndex = -1;
}

看起来你的异常在本机到托管边界时被吞掉了。我不知道这能帮多少忙,但至少给你一个开始查找问题的方向。

启用非托管代码调试,看看是否可以逐步执行库源代码,找到真正的问题所在。

enter image description here

如果有更多经验的C++/C#互操作专家想要编辑并添加更多的内容来调试问题,我将把这变成社区维基。


你好,Scott。谢谢你的回答。它肯定在某个地方出了问题,但这已经超出了我在 Visual C# - C++ 方面的能力范围,无法找到解决方案。我想我必须寻找另一个不使用 DirectShow 的网络摄像头脚本。 - Pascal

0

在设备断开连接时查找要调用的事件。编写关闭网络摄像头的代码。


嗨Gijo,不太确定你的意思是什么? - Pascal
可能有一个事件可以让你编写代码来关闭网络摄像头。或许你可以查看控件提供的事件列表。 - MACMAN
我刚刚注意到,在相机类中有一个名为“void Dispose()”的函数。如果我使用按钮关闭相机(这个功能正常工作),则不会触发此方法。但是,如果客户端断开连接,则会触发此处置函数。 - Pascal
好的,您可以在void Dispose函数中编写代码来关闭相机。 - MACMAN

0

如果仍然存在问题,请使用Touchless.RefreshCameraList断开与摄像头的连接,如果您愿意,还可以添加Touchless.CurrentCamera.Dispose。您可能仍需要启用非托管代码调试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接