2

I am trying to update a WPF textblock from within a Parallel.For and I can't. I use a dispatcher but I guess, I do it the wrong way. All of the work is done at first and then the textblock updates iteratively and fast. Here's my code:

Parallel.For(0, currentScene.Textures.Count, delegate(int i)
       {

           TextureObject texture = (currentScene.Textures[i]);

           MainWindow.Instance.StatusBarText.Dispatcher.BeginInvoke(new Action(()
               => MainWindow.Instance.StatusBarText.Text = "Loading Texture " + i
               + " - " + texture.Name ), null);
           LoadTexture(texture);
           }
       });
muku
  • 238
  • 1
  • 7
  • 20

3 Answers3

1

The Parallel.For call is itself being made on your UI thread, blocking that thread from updating until the call returns. Do this instead:

    Task.Create(delegate   
    {   
       Parallel.For( /* your current code */ );
    });   

The BackgroundWorker class may be a more appropriate solution for this scenario though...

Reference: http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/38d7a436-e1d1-4af8-8525-791ebeed9663

Robert Levy
  • 28,747
  • 6
  • 62
  • 94
  • So as I answered to Paul below, running the loading in the background results in a huge loss in performance. Plus, Task.Create() must be in an older .net revision. Do you think it will be possible somehow, to load in parallel the textures and also update the UI without measurable compromises in speed? I hope I don't ask for much ;) – muku Jan 30 '11 at 14:05
0

Robert's right, but here's how I would write it:

Enumerable.Range(0, currentScene.Textures.Count).Select(i =>
    new Task(() => {
       TextureObject texture = (currentScene.Textures[i]);

       MainWindow.Instance.Dispatcher.BeginInvoke(new Action(()
           => MainWindow.Instance.StatusBarText.Text = "Loading Texture " + i
           + " - " + texture.Name ), null);
       LoadTexture(texture);
    });
).Run(x => x.Start());

No need to create a Task whose sole job is to sit and wait on other Tasks.

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • I use .net 4 and Task.Create isn't available. I tried to change this code with Task.Factory.StartNew() and although the textblock was updating, the time for the execution of this code was increased significantly, it even hangs some times. I believe that running the loading in the background allows other heavy operations to start, like the rendering of the textures which probably drop loading speed or even kill the loading process. FYI, loading the current scene's textures on a single thread takes 58s, with parallel.For it takes 34s. Is there a way to keep performance and update the textblock? – muku Jan 30 '11 at 13:42
  • Fixed. I don't know why this would run any slower than Parallel.For. If you've got VS2010 Ultimate edition, the Concurrency profiler can help you a lot here – Ana Betts Jan 30 '11 at 20:36
0

As Mr. Levy points out any call to Parallel.For() will be a blocking call until all the loop iterations have completed. As such, you can do what was suggested above or simply wrap the invocation in a background thread.

ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object arg)
{
    Parallel.For(0, currentScene.Textures.Count, delegate(int i)        
    {
        // The rest of your code .....
    }
}));
BonanzaDriver
  • 6,411
  • 5
  • 32
  • 35
  • Hey thanks. I tried these methods but running the computations in a background thread cause a big delay in the loading process. My intention is to load the textures as fast as possible and update the UI in the same time. I guess this mustn't be possible :( – muku Jan 31 '11 at 12:17
  • How many cores are on your computer? Also, what is the total time using your original implementation versus the Parallel implementation? – BonanzaDriver Jan 31 '11 at 20:19
  • I have a quad core cpu. The total time without Parallel.For is 58s and running in parallel the time is reduced to 34s – muku Feb 02 '11 at 18:20