线程拼图 - 从UI线程等待线程(C#)

时间:2018-02-01 08:30:28

标签: c# wpf multithreading task infinite-loop

我有一个奇怪的鸡蛋类问题:

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

但是,线程使用UI中的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
}

2 个答案:

答案 0 :(得分:1)

您可以简单地在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();
}

答案 1 :(得分:0)

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