4

I have a large WPF MVVM application (over 100 windows currently and growing.) Although I try to do everything I can on background threads there always comes a time the results must be sent back to the UI thread to be displayed. When you have many windows doing this at the same time it can effect performance.

I've tried to run each window on a separate UI thread in the past but ran into so many threading issues I had to revert back to WPF's default model of only 1 UI thread per app.

I know with windows 10 coming many users will open even more windows on separate desktops and thus make this worse.

Anyone know how to get multiple UI threads to work correctly in WPF? Or have any info I can investigate to help get my app further down this road?

I approach I tried in the past was to do something similar to this:

private void OnCreateNewWindow(
   object sender,
   RoutedEventArgs e)
  {
   Thread thread = new Thread(() =>
    {
     Window1 w = new Window1();
     w.Show();

     w.Closed += (sender2, e2) =>
      w.Dispatcher.InvokeShutdown();

     System.Windows.Threading.Dispatcher.Run();
    });

   thread.SetApartmentState(ApartmentState.STA);
   thread.Start();
  }

This was about 2 years ago and I can no longer recall all the issues I faced using this unfortunately.

Is there another way? Has anyone gotten an app with many windows to work correctly using this approach or another?

2
  • When you have many windows doing this at the same time it can effect performance - so you're doing 100 windows simultaneously? I seriously doubt your end user is able to handle that. Commented Feb 4, 2015 at 5:07
  • It's a financial application spread across 8-10 screens. The frequency of updates is different for each one, some sub-second others need to be refreshed manually. But this is not uncommon in finance. Commented Feb 4, 2015 at 5:24

1 Answer 1

6

In general, it "works". The main thing you have to do is set the thread to STA (as in your example). But you gain little or nothing by running some of the UI in a different thread. Each thread can still be blocked by long-running tasks, so you still need to execute those in yet another thread, and you still have the cross-thread issue requiring some kind of marshaling back to the UI thread (e.g. Dispatcher.Invoke()).

Furthermore, with more than one UI thread, now not only do you have to keep track of which UI thread goes with which UI object (since they still can be used only with the thread that owns them), you will have more problems with UI objects interacting with each other, because those owned by different threads are mutually exclusive. Each is required to be accessed only in the thread in which it's owned, so the only way to have them work together is to create some kind of proxy system to pass data and events back and forth between threads.

Basically, it never was and still is not a good idea to create more than one thread for the UI.

Fortunately, as of .NET 4.5 and C# 5.0, there are framework and language features that greatly simplify the handling of background operations and the marshaling of information back to the UI thread. With the async/await feature, you can initiate asynchronous operations with framework features like the Task<T> class or certain class methods (usually with names ending in the word Async), have the UI thread unblocked for the duration of the operation, and yet easily write code to handle whatever work has to be done at the end of the operation.

There is also the Progress<T> class, which implements the IProgress<T> interface in a way that is convenient for dealing with UI progress updates, i.e. invokes the callback on the UI thread (as long as you create the Progress<T> instance in the UI thread, of course).

So, take the path that .NET and C# are encouraging you to take, and avoid the one that is hard. Keep all your UI in a single thread, and solve whatever issues come up using the tools provided instead of trying to fight the API. :)

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the great description. I had hoped for something else but this mirrors my experience. I'm already using async/await and Task heavily but actually haven't heard of Progress so I'll have to look into that. Thanks again.
There is one situation where it might make sense. If you have a long-running initialization sequence that is configuring things on the primary UI, you can't just offload that to a background thread, because it needs to be happening in the UI context. But, it blocks the UI context while running and causes the application to be temporarily unresponsive. Creating a progress dialog on a separate thread allows you to do that main UI thread initialization while still having a responsive UI to report progress.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.