线程谜题 - 等待来自 UI 个线程的线程 (C#)

Thread puzzle - waiting for thread from UI thread (C#)

我有一个奇怪的先有鸡还是先有蛋的问题:

我有一个从相机拍摄图像并将它们放在 UI (WPF) 上的任务。 当我退出应用程序 window 时,我想正确关闭相机,所以我调用了 Dispose()。但是,如果缓冲区中仍有图像,相机将无法关闭,所以我必须等待该线程完成(我通过更改 "isLive" 布尔值触发线程停止)。

但是,线程使用了从 UI 到 post 图像的 Dispatcher。从 UI 线程调用线程上的 "Wait()" 会使它停止 UI 线程,这反过来会在尝试更新 UI 时停止相机线程,使其挂起无限期地。

下面是我的代码的一个简单版本。有解决这个问题的技巧吗?

public void GoLive()
{
    isLive = true;
    cameraTask = Task.Run(new Action(() =>
    {
        cam.BeginAcquisition();
        while (isLive)
        {
            var rawImage = cam.GetNextImage(); // gets raw img from camera buffer

            // now make image appear on my UI
            UIDispatcher.Invoke(new Action(() =>
            {
                // this happens on the UI thread 
                uiImage = ConvertRawToBitmapSource(rawImage);
            }));
        }
}

// when you click to exit the window, this Dispose() method gets called:

public void Dispose()
{
    isLive = false; // a field boolean
    cameraTask.Wait(); // <------ hangs here infinitely!!!
    cam.DeInit(); // shut down the camera
}

您可以将 UIDispatcher.Invoke 更改为 UIDispatcher.BeginInvoke 以避免同步调用 UI 线程。然后,但是,您需要确保在 UI 线程转换图像时 rawImage 仍然有效。因此,必须将 rawImage 的所有权移至 Action 对象以避免数据竞争。

您可以在 UI 线程中简化 运行 采集循环,并在单独的任务中获取每一帧。现在您需要做的就是设置 isLive = false:.

public async Task RunAcquisition()
{
    cam.BeginAcquisition();

    while (isLive)
    {
        var rawImage = await Task.Run(() => cam.GetNextImage());

        uiImage = ConvertRawToBitmapSource(rawImage);
    }

    cam.DeInit();
}

相机甚至可能有一个可等待的 GetNextImageAsync 方法,这将进一步简化您的循环:

public async Task RunAcquisition()
{
    cam.BeginAcquisition();

    while (isLive)
    {
        var rawImage = await cam.GetNextImageAsync();

        uiImage = ConvertRawToBitmapSource(rawImage);
    }

    cam.DeInit();
}