25

I know that if I am modifying a control from a different thread, I should take care because WinForms and WPF don't allow modifying control's state from other threads.

Why is this restriction in place?

If I can write thread-safe code, I should be able to modify control state safely. Then why is this restriction present?

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
TalentTuner
  • 17,262
  • 5
  • 38
  • 63
  • I believe one of the reasons this restriction exists is so that there is always no more and no less than one thread guaranteed to be listening to the Windows message loop. – Tim M. Sep 25 '10 at 15:43
  • 1
    @Tim: That's not right, multiple UI threads can exists, each with its own message loop. For example, it is allowable to spin up a second thread to display a progress bar dialog. – Ben Voigt Sep 25 '10 at 16:11
  • @Ben: this post is tagged with c#. In a .Net application, you must always marshal control back to the GUI thread to perform drawing-related work (or risk erratic behavior or an exception). Of course, this doesn't preclude you from spawning any number of threads to perform background work. Regarding listening to the actual Win32 messages incoming, the only way I know to do that (apart from an extern call) is with "protected override void WndProc(ref Message m)". In theory, could take the messages passed to this method and use them on any thread. – Tim M. Sep 25 '10 at 16:33
  • 3
    @Tim: You keep talking about "the GUI thread". There can easily be multiple GUI threads in .NET, but the GUI components must be totally partitioned between them. GUI components created on a secondary thread cannot be directly used from the main thread. In .NET, a thread runs a message loop when you call `Application.Run`, and this can be done on multiple threads. – Ben Voigt Sep 25 '10 at 16:46
  • Application.Run launches a separate Form with its own Main() on a different thread. On a single form, there will be one thread matched to one message queue for processing events for the UI. Controls must be matched to the thread that created them. So yes, you could have multiple forms, each on different threads and processing UI activity on that thread. However, unless a control is dynamically created on another thread, it will need to be accessed from the thread designated to its parent form as the UI thread. There may be other ways around this (my original comment started with "I believe"). – Tim M. Sep 25 '10 at 17:07
  • @Ben, BTW not disagreeing with your info, mainly trying to make sure that someone doesn't read this thread and start arbitrarily spinning up multiple threads without taking the time to read about Begin/EndInvoke, the Windows message loop, etc. – Tim M. Sep 25 '10 at 17:17

6 Answers6

27

Several GUI frameworks have this limitation. According to the book Java Concurrency in Practice the reason for this is to avoid complex locking. The problem is that GUI controls may have to react to both events from the UI, data binding and so forth, which leads to locking from several different sources and thus a risk of deadlocks. To avoid this .NET WinForms (and other UIs) restricts access to components to a single thread and thus avoids locking.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • Add to that the interoperability with ActiveX controls (Brower control etc. - possibly indirectly through winforms integration) and you also have a COM legacy that is Single Threaded Appartement. – TomTom Sep 25 '10 at 16:38
  • 3
    Windows **doesn't** restrict access to the owning thread. That limitation is introduced by .NET. – Ben Voigt Sep 25 '10 at 16:46
  • @Ben: I didn't know that. I'll update my answer to reflect that. I'll check what the book says. – Brian Rasmussen Sep 25 '10 at 16:59
  • @Ben: I just checked the book, and it doesn't say that Windows does this, so the fault is all mine. Anyway, the reasoning still applies as restricting access to a single threads does avoid locking, so I hope the answer is still useful. Thanks for the info - I've been on .NET for too long, so I just assumed this was a Windows thing. – Brian Rasmussen Sep 25 '10 at 17:06
  • @Ben: in addition, if I recall correctly, .NET didn't even enforce this in the debugger (via `Control.CheckForIllegalCrossThreadCalls`) until 2.0. – Michael Petrotta Sep 25 '10 at 17:23
9

In the case of windows, when a control is created UI updates are performed via messages from a message pump. The programmer does not have direct control of the thread the pump is running on, therefore the arrival of a message for a control could possibly result in the changing of the state of the control. If another thread (that the programmer was in direct control of) were allowed to change the state of the control then some sort of synchronization logic would have to be put in place to prevent corruption of the control state. The controls in .Net are not thread safe; this is, I suspect by design. Putting synchronization logic in all controls would be expensive in terms of designing, developing, testing and supporting the code that provides this feature. The programmer could of course provide thread safety to the control for his own code, but not for the code that is in .Net that is running concurrently with his code. One solution to this issue is to restrict these types of actions to one thread and one thread only, which makes the control code in .Net simpler to maintain.

Steve Ellinger
  • 3,957
  • 21
  • 19
3

.NET reserves the right to access your control in the thread where you created it at any time. Therefore accesses that come from another thread can never be thread safe.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2

You might be able to make your own code thread-safe, but there is no way for you to inject the necessary synchronization primitives into the builtin WinForm and WPF code that match up with the ones in your code. Remember, there are a lot of messages getting passed around behind the scenes that eventually cause the UI thread to access the control without you really ever realizing it.

Another interesting aspect of a controls thread affinity is that it could (though I suspect they never would) use the Thread Local Storage pattern. Obviously if you accessed a control on a thread other than the one it was created on it would not be able to access the correct TLS data no matter how carefully you structured the code to guard against all of the normal problems of multithreaded code.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
2

Windows supports many operations which, especially used in combination, are inherently not thread-safe. What should happen, for example, if while one thread is trying to insert some text into a text field starting with the 50th character, while another thread tries to delete the first 40 characters from that field? It would be possible for Windows to use locks to ensure that the second operation couldn't be begun until the first one completed, but using locks would add overhead to every operation, and would also raise the possibility of deadlock if actions on one entity require manipulation of another. Requiring that actions involving a particular window must happen on a particular thread is a more stringent requirement than would be necessary to prevent unsafe combinations of operations from being performed simultaneously, but it's relatively easy to analyze. Using controls from multiple threads and avoiding clashes via some other means would generally be more difficult.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

Actually, as far as I know, that WAS the plan from the beginning! Every control could be accessed from any thread! And just because thread locking was needed when another thread required access to the control --and because locking is expensive-- a new threading model was crafted called "thread rental". In that model, related controls would be aggregated into "contexts" using only one thread, thus reducing the amount of locking needed. Pretty cool, huh?

Unfortunately, that attempt was too bold to succeed (and a bit more complex because locking was still required), so the good old Windows Forms threading model --with the single UI thread and with the creating thread to claim ownership of the control-- is used once again in wPF to make our lives ...easier?

Theodore Zographos
  • 2,215
  • 1
  • 24
  • 23