【问题标题】:Program hangs after calling Dispose()调用 Dispose() 后程序挂起
【发布时间】:2012-05-02 03:47:12
【问题描述】:

我正在使用 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(); 挂起。如上所述,没有错误或异常。 在onImageCapture 函数() 中调用onDataReceive() 可能是个问题。 我还在表单中添加了一个按钮来触发thrashOldCamera(),这非常有效。

非常感谢任何帮助/提示。

【问题讨论】:

标签: c# multithreading sockets webcam dispose


【解决方案1】:

这称为死锁,一个典型的线程问题。我没有看到您在 sn-p 中的任何地方显式调用 UI 线程,因此死锁可能位于相机固件本身。核心问题是您试图关闭相机它的回调仍在执行,没有很多代码可以适应这种情况。在回调完成之前,Release() 调用无法完成。但是直到 Release() 调用完成,回调才能完成。死锁城市。

您需要重新构建代码,以免发生这种情况。延迟释放相机是关键,最好在打开相机的同一线程上完成。例如,您可能会通过在 FormClosed 事件中释放它来解决它。或者根本不释放它,让 Windows 自动关闭句柄。

【讨论】:

    【解决方案2】:

    不知道这真的应该是评论还是答案,但至少我可以让你走上正确的道路。

    found the source 到您正在使用的库。这是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;
    }
    

    您的异常似乎在本机到托管边界期间被吃掉了。我不知道这对你有多大帮助,但至少它让你开始寻找。

    启用非托管代码调试,看看您是否可以单步调试库源代码,看看真正的问题出在哪里。

    如果其他在 C++/C# 互操作方面有更多经验的人想要编辑此内容并添加更多接下来要执行的操作以调试问题,我将把它作为社区 wiki。

    【讨论】:

    • 你好斯科特。谢谢你的回答。它肯定会在某处中断,但这超出了我的视觉 c# - c++ 级别来找到解决方案。我想我必须寻找另一个不使用 directShow 的网络摄像头脚本。
    【解决方案3】:

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

    【讨论】:

    • 嗨 Gijo,不太清楚你的意思是什么?
    • 可能有一个事件可用于编写关闭网络摄像头的代码。也许您可以检查控件可用的事件列表。
    • 所以我刚刚注意到,Camera 类中有一个名为“void Dispose()”的函数。如果我使用按钮打开相机 - 效果很好 - 这个方法不会被触发。如果客户端断开连接,它会触发这个 dispose 函数
    • 好的,你可以在void Dispose函数上写代码来关闭相机。
    【解决方案4】:

    以防万一仍然存在问题,请使用 Touchless.RefreshCameraList 断开与相机的连接,如果您还可以添加 Touchless.CurrentCamera.Dispose所以希望。您可能仍然需要启用无人代码调试

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多