Click here to Skip to main content
15,894,017 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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!

C#
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;
        }
Posted
Updated 14-Jan-15 12:28pm
v2

1 solution

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
 
Share this answer
 
v6
Comments
TheRealSteveJudge 15-Jan-15 9:54am    
5*
Sergey Alexandrovich Kryukov 15-Jan-15 11:50am    
Than you, Steve.
—SA
TheRealSteveJudge 16-Jan-15 2:48am    
You're welcome!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900