Reputation: 26299
This msdn article states:
The WaitAll method is not supported on threads that have STAThreadAttribute.
If one googles or searches stackoverflow they will find numerous workarounds.
I would like to know the reason this is not supported. Surely this is not arbitrary and there is a good reason why that might be the case. What is that about STA thread that prevents or makes it more difficult for WaitAll
to do its job?
Can you explain?
Upvotes: 4
Views: 102
Reputation: 942109
The [STAThread] attribute is a promise, cross your heart hope to die. It tells the OS that you created a thread that is a hospitable home to code that is not thread-safe. The promise you make is that the thread has a dispatcher loop (aka message loop) and never blocks. The dispatcher loop solves the producer-consumer problem, necessary to ensure that a thread-safe call can be made even when the original call is made from a worker thread. The never-blocks requirement ensures that such calls can be marshalled when necessary and the risk for undiagnosable deadlock is low.
In other words, not only WaitAll() is illegal, even using WaitOne() is formally verboten.
Avoiding WaitOne() is not very difficult, but there are other blocking scenarios that are common and hard to avoid. Thread.Join() is one for example. So instead of just outright forbidding all of it, the CLR designers came up with a solution to support this kind of limited blocking. There is a dispatcher loop inside the CLR that takes over the role of the loop that you started with Application.Run(). It is roughly similar to Application.DoEvents(), but not quite as dangerous by filtering the kind of messages that so commonly cause re-entrancy bugs.
Whether that was a good design decision is pretty debatable btw. The general problem is that programmers accidentally rely on it and are in for a very rude surprise when they discover that these waits don't actually block. Debugging re-entrancy bugs is excessively little joy and fixing the bug always requires a non-trivial rewrite. The CLR designers only did this to make it easier to port code, not to provide a feature. Notable is that this code was never published, not in SSCLI and not in CoreCLR, they don't want anybody to intentionally take advantage of it.
They could easily have made WaitAll() work that way as well, in spite of the assertion in the blog post. But they put their foot down on that one, that's too much help.
And too much help as-is. Where ever you might consider using WaitOne(), always move the code after it into a BackgroundWorker.RunWorkerCompleted event handler or a Task continuation obtained from TaskScheduler.FromCurrentSynchronizationContext() or after an await in an async method.
Upvotes: 3