我应该如何加载图像一次,并在多线程c#应用程序中传递它

本文关键字:应用程序 多线程 一次 何加载 加载 图像 我应该 | 更新日期: 2023-09-27 18:08:00

我正在编写一个应用程序,其目的是找到文件夹中的图像,并将它们呈现给用户,询问他们应该如何裁剪。由于我每天从一种语言跳到另一种语言,我倾向于把我的最佳实践和概念混在一起,这就是这种情况。

程序流程是这样的:呈现给您一个主表单和一个浏览按钮(您可以选择将文件夹放到该按钮上)。当您选择一个文件夹时,它会打开另一个表单,扫描所选文件夹中的.jp[e]g和.tif[f]图像,并将它们显示在一个列表中,询问您想要裁剪哪些图像,以及其他一些选项。然后它返回到主表单,该表单遍历从该表单中选择的图像列表,并在BackgroundWorker中为每个图像加载到裁剪表单中,然后裁剪并保存它。

对于原型,我处理图像的加载有点混乱;它为选择表单加载文件夹中的所有图像,然后在裁剪器中依次加载每个选中的图像,然后在主表单中再加载一次以进行裁剪和保存。对于大型图像,这可能会使任务耗时,因为在最坏的情况下,该应用程序将处理高达4000px2的TIFF源图像。

我想认为有一种方法可以将每个Bitmap一次加载到某种全局数组中,但那将是Visual Basic或VB for Applications。因为我使用的是BackgroundWorker,我也不得不担心线程安全,以及c#是否会对我大喊大叫,试图以不安全的方式访问某些东西。关于如何在限制单个图像加载到Bitmap对象的次数的同时完成应用程序的目的,有什么想法吗?

我应该如何加载图像一次,并在多线程c#应用程序中传递它

由于根据微软的Bitmap类实例成员不保证是线程安全的,我看到两个选项:

  • 性能,但有点风险
    将所有内容加载为位图对象,并将所有这些位图对象添加到ConcurrentDictionary…访问是线程安全的,非常快,因为实现大多是无锁的…

  • 性能稍差,但没有风险
    将所有文件作为MemoryMappedFile加载到ConcurrentDictionary中。当你需要一个位图时,你只需要创建一个MemoryMappedFile(它将使用你之前创建的那个已经占用的相同内存,因此非常快),让位图从那里加载它的内容…当你的应用程序关闭或者你想要清理时,你只需要通过字典并摆脱那些MemoryMappedFile对象…

你有两个选择:一个比另一个更面向对象,但更难实现:

A)创建一些类(例如,Document, BitmapDocument或其他),其中包含一个列表,您添加所有的图像。不要担心多线程,只要在每次添加图像时锁定List即可。根据您对位图的处理,您可能还需要锁定它们。(锁定在c#中并不难,例如lock(someVariable){…})在不同的控件使用位图时复制位图(已经加载到List内部)不应该需要太多的内存/时间(因为这些将是RAM操作)。当主应用程序必须将这个"文档"类传递给所有子控件和表单时,问题就变得复杂了。一旦每个控件都知道了"document"实例,对它的任何添加都立即可用。

B)创建一些类,其中列表被标记为内部和静态的,这样项目中的任何类都可以查看和使用它。这样,您使用的是一种"全局"数组,但不是完全全局的。它只在你的项目中可见(由于"内部"标签),它仍然保存在一个类中。注意,你仍然需要使用c#的锁机制来保证所有的线程安全。

注意,不要直接访问类成员,可以考虑编写方法来操作它们并适当地锁定对象。(这将是正确的编程实践。)对于B,只创建标记为"internal"answers"static"的方法。

我个人会使用B(实现和调试更快),特别是在原型设计中,因为使用它可以使将来转向A并不太难。

此外,如果你使用(B)的内部静态对象和函数的方法,你可以编写一些函数,如GetBitmap(string path),从磁盘加载图像,并保存所有最近加载的图像的缓冲区,这样线程不必从磁盘加载两次。