尝试使用WPF异步加载图像 [英] Trying to load images asynchronously using WPF

查看:383
本文介绍了尝试使用WPF异步加载图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将一堆图像(大约1000+)加载到页面上。这些图像中的每一个都来自不同的文件。他们实际上在wpf中加载相当快,没有任何技巧,大约0.5秒。不幸的是,这还不够快。我尝试做一些异步的东西(第一次定时器)让它工作,但我不能。我最终得到一个例外,说我试图在2个线程之间共享一个对象(真的吗?不是那个点吗?)。不管怎么说,一定有些傻我想念 - 帮忙?!



我留下了一些代码,专注于重要的东西,简化了图像的定位。



除了 - 我还找到了对bitmap.SetSourceAsync()的引用,但是在我的生活中找不到正确的dll引用。



谢谢!



  class  AsyncRenderer {
private _canvas; // 在构造函数中加载
private _imagePaths;

public async RenderImages(){
int ct = 0 ;
int width = 40 ;
foreach var f in _imagePaths)
{
AddImage(f, new Point((width + 5 )* ct, 10 ),宽度);
ct ++;
}
}

私有 async void AddImage( string filename,Point location, int width)
{
Image myImage = new Image();
myImage.Width = width;

// 创建源
BitmapImage myBitmapImage = await GetBitmap(filename);
// set image source
myImage.Source = myBitmapImage;

Canvas.SetTop(myImage,location.Y);
Canvas.SetLeft(myImage,location.X);

_canvas.Children.Add(myImage);
}

private async 静态任务< BitmapImage> GetBitmap( string filename)
{
任务< BitmapImage> t = Task.Run(()= > {
BitmapImage myBitmapImage = new BitmapImage( );

// BitmapImage.UriSource必须位于BeginInit / EndInit块
myBitmapImage.BeginInit();
myBitmapImage.UriSource = new Uri(filename);

myBitmapImage.DecodePixelWidth = 200 ;
myBitmapImage.EndInit();

return myBitmapImage;
});

await t;
return t.Result;
}

解决方案

对不完整的答案感到抱歉,这需要我做一些额外的研究和开发,但我会对你这样做的方法不够热情。我不确定你需要做些什么特别的事情。我将尝试解释。



您无法在线程之间共享UI对象。这不是很明显的原因吗?您可以使用 Dispatcher.BeginInvoke 委托将对象添加到UI线程的方法,但它会增加线程的事件队列,因此它可能会破坏您改进的目的。



你需要了解你想要提高的吞吐量。如果您有一个额外的CPU内核来并行处理您的处理利用更多的硬件资源,您可以真正提高总吞吐量。情况并非总是如此:额外的核心可能不存在或忙于其他进程。由于线程和异步/等待机制的一些开销,您甚至可以增加总的初始化时间。您真正想要的是不同的:尝试将UI 初始化为响应时,并在所有细节中显示窗口视图的可见部分,然后在用户关注时并行完成进一步渲染关于等待UI初始化的主要部分的性能。



请注意 WPF渲染已经并行化。这是通过具有单独的UI线程(这是调用 Application.Run 的线程)和另一个, 渲染线程 <来完成的。 / i>的。所以,首先,你需要清楚地理解线程模型 http://msdn.microsoft.com/en-us/library/ms741870%28v=vs.110%29.aspx [ ^ ]。



如果您将这样一个简单的应用程序编写为基本文本编辑器,则可以清楚地看到渲染在单独线程中的效果。在初始化时,处理命令行参数并在编辑器窗口中加载一个大文件(你可以在事件的句柄中执行它 Window.ContentRendered 或重写方法 Window.OnContentRendered ,用于查看渲染除文件缓冲区内容以外的所有内容时的效果)。可能只有几兆就足够了。您将看到,您可以比到达最后一页更早地访问文档的顶部滚动页面。这是你用图像达到的最佳效果。



参见这些文章:

http://msdn.microsoft.com/en-us/magazine/cc163328.aspx [ ^ ],

http://mrpfister.com/journal/improving-wpf-rendering-performance [ ^ ]。



- SA

I'm trying to load a bunch of images (around 1000+) onto a page. Each of these images come in a different file. They actually load fairly fast in wpf without any tricks, in around 0.5s. Unfortunately this is not fast enough. I tried to do some async stuff (first timers) to make it work, however I can't. I end up with an exception saying that I'm trying to share an object between 2 threads (really? isn't that the point?). Anyway, there must be something silly I'm missing - help?!

I left some code out to focus on the important stuff and simplified the positioning of the images.

Aside - I also found a reference to bitmap.SetSourceAsync(), but could not for the life of me find the correct the dll to refer to.

Thanks!

class AsyncRenderer {
        private _canvas; // loaded at constructor
        private _imagePaths;

        public async RenderImages() {
           int ct = 0;
           int width = 40;
           foreach (var f in _imagePaths)
           {
              AddImage(f, new Point((width + 5) * ct, 10), width);
              ct++;
           }
        }

        private async void AddImage(string filename, Point location, int width)
        {
            Image myImage = new Image();
            myImage.Width = width;

            // Create source
            BitmapImage myBitmapImage = await GetBitmap(filename);
            //set image source
            myImage.Source = myBitmapImage;

            Canvas.SetTop(myImage, location.Y);
            Canvas.SetLeft(myImage, location.X);

            _canvas.Children.Add(myImage);
        }

        private async static Task<BitmapImage> GetBitmap(string filename)
        {
            Task<BitmapImage> t = Task.Run(() => {
                BitmapImage myBitmapImage = new BitmapImage();

                // BitmapImage.UriSource must be in a BeginInit/EndInit block
                myBitmapImage.BeginInit();
                myBitmapImage.UriSource = new Uri(filename);

                myBitmapImage.DecodePixelWidth = 200;
                myBitmapImage.EndInit();

                return myBitmapImage;
            });

            await t;
            return t.Result;
        }

解决方案

Sorry for the incomplete answer, which would require me to do some extra research and development, but I would be not enough enthusiastic with your approach to do that. I'm not sure you need to do something special about it. I'll try to explain.

You cannot share the UI object between threads. Isn't that obvious why? You can delegate the method adding the object to UI thread using Dispatcher.BeginInvoke, but it will increase the event queue of the thread, so it can defeat the purpose of your improvement.

You need to understand what throughput you are trying to improve. If you have an extra CPU core to handle your processing in parallel utilized more hardware resource, you can really improve the total throughput. This is not always the case: the extra cores might be absent or busy with other processes. You can even increase the total initialization time, due to some overhead of threading and async/await mechanism. What you really want is different: trying to initialize the UI to the point when it becomes responsive and show the visible part of the window's view in all detail and then complete further rendering in parallel, when the user cares about performance less then about waiting for primary part of UI initialization.

Note that WPF rendering is already parallelized. This is done through having separate UI thread (this is the thread where Application.Run is called) and another one, rendering thread. So, first of all, you need to clearly understand the threading model: http://msdn.microsoft.com/en-us/library/ms741870%28v=vs.110%29.aspx[^].

You can clearly see the effect of rendering in a separate thread if you write such a simple application as a basic text editor. On initialization, process command-line parameters and load a big file in the editor window (you can do it in the handle of the event Window.ContentRendered or overridden method Window.OnContentRendered, to see the effect of rendering when everything except the file buffer content is rendered). Probably, just few megabytes would be enough. You will see that you can access the top scroll page of the document much earlier than you can get to the very last page. This is the best you can achieve with your images.

See also these articles:
http://msdn.microsoft.com/en-us/magazine/cc163328.aspx[^],
http://mrpfister.com/journal/improving-wpf-rendering-performance[^].

—SA


这篇关于尝试使用WPF异步加载图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆