如何在 C# 的后台线程中创建 BitmapImage 缓冲区? [复制]
Posted
技术标签:
【中文标题】如何在 C# 的后台线程中创建 BitmapImage 缓冲区? [复制]【英文标题】:How to create a BitmapImage buffer in background thread on C#? [duplicate] 【发布时间】: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;
);
【问题讨论】:
【参考方案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,您帮助我解决了这个问题并学习了新知识。以上是关于如何在 C# 的后台线程中创建 BitmapImage 缓冲区? [复制]的主要内容,如果未能解决你的问题,请参考以下文章