【问题标题】:How to create a BitmapImage buffer in background thread on C#? [duplicate]如何在 C# 的后台线程中创建 BitmapImage 缓冲区? [复制]
【发布时间】:2021-11-21 04:56:49
【问题描述】:

您好,我想创建一个 BitmapImage 缓冲区,该缓冲区使用来自后台工作人员的队列构建。主要目标是从专用网络加载一些图像并且不阻塞 UI。我可以在不阻塞 UI 的情况下创建该缓冲区,但是当我尝试从队列中获取一个图像时,我得到一个 System.InvalidOperationException,因此我试图访问另一个线程拥有的对象。

我的工人代码:

        private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
        {
            return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
            {
                var parameters = CommonData.CurrentRecipe.Parameters["validation"];
                var buffer = new Queue<BitmapImage>();

                foreach (var label in labels)
                {
                    var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
                    var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
                    var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);

                    try
                    {
                        if (File.Exists(fullPath))
                        {
                            buffer.Enqueue(new BitmapImage(new Uri(fullPath, UriKind.Absolute)));
                        }
                        else
                        {
                            throw new ValidationImageNotFoundException(fullPath);
                        }
                    }
                    catch { }
                }

                return buffer;
            });
        }

调用方法:

        private async void LoadValidationImages()
        {
            var imageList = new List<ReadLabel>(CommonData.CurrentFiche.Labels);
            var images = imageList.FindAll(f => f.CoscNumber.StartsWith("err"));

            if (images.Count > 0)
            {
                Queue<BitmapImage> result = await BufferLoader(images);

                ImageBuffer = new Queue<BitmapImage>(result);

                BufferLoadingCompleted();
            }
        }

UI线程调用方法:

        private void BufferLoadingCompleted()
        {
            /*Dispatcher.Invoke(() =>
            {*/
                imgToValidate.Source = ImageBuffer.Peek();

                var parameters = CommonData.CurrentRecipe.Parameters["validation"];
                rotation = parameters[ValidationParameters.Parameters.ImageRotation.ToString()].ValueToDouble();
                scaleX = parameters[ValidationParameters.Parameters.ImageScaleX.ToString()].ValueToDouble();
                scaleY = parameters[ValidationParameters.Parameters.ImageScaleY.ToString()].ValueToDouble();
                scrlImage.ScrollToHorizontalOffset(parameters[ValidationParameters.Parameters.ScrollerHorizontalFactor.ToString()].ValueToDouble());
                scrlImage.ScrollToVerticalOffset(scrollPosVertical = parameters[ValidationParameters.Parameters.ScrollerVerticalFactor.ToString()].ValueToDouble());

                ApplyTransformations();

                Console.WriteLine("Load finished");
            //});
        }

我尝试在BufferLoadingCompleted() 上使用Dispatcher.Invoke,但它不起作用我得到了同样的异常。我做错了什么?

最终代码。安迪建议的解决方案: 在我的后台工作代码中,我没有 Freeze() 在工作线程中创建的新对象,所以我遇到了异常。

解决方案仅适用于后台worker方法:

private Task<Queue<BitmapImage>> BufferLoader(List<ReadLabel> labels)
{
    return Task<Queue<BitmapImage>>.Factory.StartNew(() =>
    {
        var parameters = CommonData.CurrentRecipe.Parameters["validation"];
        var buffer = new Queue<BitmapImage>();

        foreach (var label in labels)
        {
            var fileName = $"{CommonData.CurrentFiche.RegId}-{label.ReadPosition}.bmp";
            var validationPath = parameters[ValidationParameters.Parameters.sValidationImagesPath.ToString()].Value.ToString();
            var fullPath = Path.Combine(Properties.Settings.Default.CameraImagesBasePath, validationPath, fileName);

            try
            {
                if (File.Exists(fullPath))
                {
                    var newImage = new BitmapImage(new Uri(fullPath, UriKind.Absolute));
                    newImage.Freeze();

                    buffer.Enqueue(newImage);
                }
                else
                {
                    throw new ValidationImageNotFoundException(fullPath);
                }
            }
            catch { }
        }

        return buffer;
    });
}

【问题讨论】:

    标签: c# wpf task backgroundworker bitmapimage


    【解决方案1】:

    您在默认情况下具有线程关联性的非 ui 线程上创建一些东西。

    幸运的是,位图图像继承自可冻结。 查看继承链:

    https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.imaging.bitmapimage?view=net-5.0

    继承: 目的 调度程序对象 依赖对象 可冻结 动画 图像源 位图源 位图图像

    如果您在可冻结对象上调用 .Freeze(),那么您可以在线程之间传递它。

    https://docs.microsoft.com/en-us/dotnet/desktop/wpf/advanced/freezable-objects-overview?view=netframeworkdesktop-4.8

    从那里:

    什么是 Freezable?

    Freezable 是一种特殊类型的对象,具有两种状态:未冻结和已冻结。解冻后,Freezable 的行为似乎与任何其他对象一样。冻结后,无法再修改 Freezable。

    Freezable 提供一个 Changed 事件来通知观察者对该对象的任何修改。冻结 Freezable 可以提高其性能,因为它不再需要在更改通知上花费资源。 冻结的 Freezable 也可以跨线程共享,而未冻结的 Freezable 则不能。

    【讨论】:

    • 感谢@Andy,您帮助我解决了这个问题并学习了新知识。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多