 |
|
 |
Why don't you use BackGround Workers for updating the GUI?
It's easy and threat safe ...
Angel
|
|
|
|
 |
|
 |
Hi there,
whats the deal wrapping an access to a control in a:
Invoke(new MethodInvoker(delegate {
control.DoYourStuffs();
}));
-Block? And if you want to do something in a Thread just do the following:
ThreadPool.QueueUserWorkItem(delegate {
try {
//Do your work
} catch (Exception ex) {
// handle exceptions
}
});
Everything that is done inside of the delegate accessing a control needs to be wrapped as seen above.
For me its pretty simple. You can even model shortcuts and code-templates for them.
It's easy, it's safe.
Regards,
André
|
|
|
|
 |
|
 |
Not relating to this article, but I use the Invoke method to update a progress bar and it
working fine for me.
I changed to the construct you mentioned and my progress bar is no longer updated.
Any idea why?
Here is a function that I call from a backgroundworker
private void UpdateProgress(int value)
{
ThreadPool.QueueUserWorkItem(delegate
{
try
{
string s = string.Format("{0} of {1}", value, xpProgressBar1.PositionMax);
xpProgressBar1.Text = s;
xpProgressBar1.Position = value;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
});
if (xpProgressBar1.InvokeRequired)
{
xpProgressBar1.BeginInvoke(new SimpleCallback(UpdateProgress), value);
return;
}
string s = string.Format("{0} of {1}", value, xpProgressBar1.PositionMax);
xpProgressBar1.Text = s;
xpProgressBar1.Position = value;
}
And before you ask no I didn't have both techinques in at the same time.
modified on Tuesday, December 18, 2007 1:52:58 PM
|
|
|
|
 |
|
 |
Hi Andre,
Sorry I didn't make it clear in my original article (now updated to correct this) when this technique should be used. Here's what the updated article says:
"This technique should only be considered when one or more controls in your GUI are processing tens or hundreds of messages a second, causing the GUI to become unresponsive. Typically this is true when using real-time data feeds. The controls in question should be read-only, not updated via the GUI. In all other cases, the standard BeginInvoke/SycnchronizationContext/AsyncOperation calls should be used. For the majority of Windows Forms GUIs, this technique is not appropriate."
Have a play with the new sample application. It's thread safe and responsive, processing a large volume of updates a second.
|
|
|
|
 |
|
 |
Your technique does work for the simplest of non-ActiveX controls. For more sophisticated controls, especially those that are ActiveX, it will not work. Sometimes the failure will be spectacular, but more often just puzzling and intermittent.
My old employer had a very large application that was written in the mid-90s to call UI functions from any thread. We had all kinds of strange issues; the most infamous of which we called blackout. After running for some indeterminate period of time, one of our widgets would render itself as completely black. After that point, any control that the user interacted with would suffer blackout until the entire app was just a black rectangle. We solved that problem by painstakingly converting EVERY UI call to the UI thread.
I urge *everyone* who reads this article to refrain from using this "technique". You *will* be sorry unless your app is a simply toy as presented here. If you use grid or tree controls, for example, they'll probably fail (or just not work) much sooner than the simplest, oldest (i.e. buttons, listboxes, editboxes) Windows controls. But fail they will.
.NET makes it **SO EASY** to do a SynchronizeInvoke that you really have no excuse for not doing things the right way.
Having said all of that, are you aware that you can have multiple "UI" threads in a single app? All a "UI" thread is is one with a message pump. You get a pump simply buy calling the GetMessage, TranslateMessage, DispatchMessage Win32 API functions. In old-school unmanaged apps, it is fairly easy to create multiple UI threads. You can then create a child window in a secondary thread and parent it to a window created in the main thread. This actually works. Messages for the child window get handled in the non-Main thread, while parent messages get handled in the main thread. I've even done this in .NET 2.0.
However, this doesn't work for all UI controls, especially the DataGrid. I believe this is because the DataGrid is an ActiveX control under the hood, and it can only live in the default single-threaded apartment.
In summary, this article does tell you a stupid pet trick that you can do with simple Windows controls. Just because you can do something doesn't mean you should. It will bite you. Guaranteed.
|
|
|
|
 |
|
 |
"are you aware that you can have multiple "UI" threads in a single app?"
This seems very interesting, I haven't heard of it in any other place. Can you post some example code?
Using .NET, you normally get a System.ArgumentException ("Controls created on one thread cannot be parented to a control on a different thread").
|
|
|
|
 |
|
 |
I see a lot of posts about this being a "bad thing" and i agree in some respects. But there is a place for it and if you know what your doing it is very (and sometimes the only way) to avoid the .NET overhead.
Now this is nothing new.. if you bother to read the win32 API documentation and have actually written code that directly uses the API (no frameworks that do everything for you , MFC,ATL, .NET etc) you would have a much better understanding of how it actually works. You'd also know that the general rule is "don't call functions on a windows control from a thread that didn't created the control". this is a common thing all through windows code and it's not limited to GUI.
The article clearly states this primary rule and gives you some ideas of how to manage it in your code. .NET's way of managing it is to throw a nasty exception, which is over kill for alot of cases, but yes, it makes everyone feel nice about things and yes it makes sure the rule is enforced. and all the new programmers of the world who haven't used anything other than VB and C# can let their managers sleep well at night.
There is no way you can match the performance using only .NET BeginInvoke etc (at least in 2.0) and you'll often find that if you try your GUI will completely freeze up. You need to use less calls to BeginInvoke and update multiple controls etc with one call.. there are plenty of other ways to do it too. Now if you really need that kind of performance then it might be worth looking at the design first and fixing that. But like most things it has it's place.
Jared Allen.
www.chironexsoftware.com
|
|
|
|
 |
|
 |
Hi Jared,
Sorry I didn't make it clear in my original article (now updated to correct this) when this technique should be used. Here's what the updated article says:
"This technique should only be considered when one or more controls in your GUI are processing tens or hundreds of messages a second, causing the GUI to become unresponsive. Typically this is true when using real-time data feeds. The controls in question should be read-only, not updated via the GUI. In all other cases, the standard BeginInvoke/SycnchronizationContext/AsyncOperation calls should be used. For the majority of Windows Forms GUIs, this technique is not appropriate."
Have a play with the new sample application. It's thread safe and responsive, processing a large volume of updates a second.
|
|
|
|
 |
|
 |
I agree, the most useful place that i've used such a technique is monitoring data packets in a network application. When you are receiving 1000's of packets a second there is no way to have a responsive (if at all usable) GUI using just normal async begininvoke etc, you could do it but you need to batch your updates internally, for example only update the UI once every 5 secs etc. But if you want faster updates and a more interactive GUI then a multithreaded update approach as described in this article is a good way to go.
- (as another example, dvd or video playback. using a seperate thread to update\render the video is common. )
Jared Allen.
www.chironexsoftware.com
|
|
|
|
 |
|
 |
Thanks for posting this article. It's always good to know these undocumented techniques for getting that last ounce of optimization (quite frankly is necessary in some .NET apps).
|
|
|
|
 |
|
 |
So - as far as I can see, your whole reason is to avoid using the recommended techniques because they require you to type in a couple of extra lines. There's a reason that these calls are there, and you've either taken your extra brave pills to handle the barrage of abuse you're going to get from your customers when things start going pear shaped or you don't intend to support the customers you've just shafted.
|
|
|
|
 |
|
 |
Hi Pete,
Sorry I didn't make it clear in my original article (now updated to correct this) when this technique should be used. Here's what the updated article says:
"This technique should only be considered when one or more controls in your GUI are processing tens or hundreds of messages a second, causing the GUI to become unresponsive. Typically this is true when using real-time data feeds. The controls in question should be read-only, not updated via the GUI. In all other cases, the standard BeginInvoke/SycnchronizationContext/AsyncOperation calls should be used. For the majority of Windows Forms GUIs, this technique is not appropriate."
|
|
|
|
 |
|
 |
You said: "Please remember that as this is a technique not often seen."
Obviously, it hasn't occured to you that there's a reason why you haven't seen this technique very often.
|
|
|
|
 |
|
 |
Sorry Dave, I didn't make it clear in my original article (now updated to correct this error) but this technique should only be used in performance critical situation where you don't want to force all GUI updates through a single thread. It should not be used in most situations. It is perfectly thread safe provided the guide lines are followed.
|
|
|
|
 |
|
 |
I dont like the idea of turning off a security check, its not cool.
You should be used old fashioned InvokeRequired/Invoke with delegates, or anonomous delegates if you prefer.
Or better still with .NET 2.0 use SynchronizationContext or AsyncOperation and AsyncOperationManager Classes. These are the preferred ways.
Have a look at
this article
this MVP Blog
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
My Blog : sachabarber.net
|
|
|
|
 |
|
 |
Sorry Sacha, I didn't make it clear in my original article (now updated to correct this error) but this technique should only be used in performance critical situation where you don't want to force all GUI updates through a single thread. It is perfectly thread safe provided the guide lines are followed.
|
|
|
|
 |
|
 |
"years ago when I was writing 16-bit C Windows applications, it was not uncommon to associate several threads with several of the application's child windows in order to improve the responsiveness of the GUI"
that was highly uncommon since 16 bit windows did not know threads. what we did back then were either state machines or calls to process message from your code.
with state machines you would write a code (for example z-modem protocol) that would execute part and then return current state. you would then call it like this.
state=start_state;
while (state!=end_state) {
state=zmodem_transfer(state);
process_messages()
}
the other way - calling yield from you own code as often as possible (as part of every major loop) to yield processor time would commonly be implemented by passing a pointer to function
extern void (*yield)(status_struct *status);
...
while (zmodem_transfer(yield));
in the yield function you would typically process windows messages. but this would not work with third party libs.
last but not least without yielding control to message pump every now or then windows would freeze.
...haven't written a c/c++ code for a while so excuse possible errors.
modified on Saturday, December 08, 2007 1:17:05 AM
|
|
|
|
 |
|
 |
I believe the only reason why microsoft left in the CheckForIllegalCrossThreadCalls was to stop the flood of developers whining that their non thread safe code would throw tons of exceptions after the upgrade from .net 1 - .net 2.
In dev/testing apps and tools its great for getting the job done quickly without writing delegates - but I wouldn't even think twice about using it for a production application - too risky.
Dank_Army
|
|
|
|
 |
|
 |
Your solution is to turn off the thread check? That defeats the whole purpose, because the OS and framework can do something to the list that is out of your control. What if something in the binding source changes? What if the user clicks on a header to resort the data?
This, IMO, is a very BAD approach. And even worse, this crossthread checking is only done in debug only!
Marc
|
|
|
|
 |
|
 |
The .NET WinForms controls are not implemented in a thread safe manner.
From MSDN[^]:
When a thread other than the creating thread of a control tries to access one of that control's methods or properties, it often leads to unpredictable results. A common invalid thread activity is a call on the wrong thread that accesses the control's Handle property. Set CheckForIllegalCrossThreadCalls to true to find and diagnose this thread activity more easily while debugging.
(emphasis added)
i.a.W. The recommended method may fail anytime (seemingly randomly). The flag you recommend to set to false is merely for debugging and diagnostics.
The correct way is to use BeginInvoke, preferrably in chunks, as an invoke for each single item may be costly.
|
|
|
|
 |
|
 |
I've updated the introduction to indicate under what circumstances this technique should be use. It is not for every day use. If the CheckForIllegalCrossThreadCalls is not set to false the code will fail in a release version at run-time.
The reason BeginInvoke is not used is that the only reason to consider using this technique is in performance critical situation where you don't want to force all GUI updates through a single thread.
As for your concerns on thread safety, the sample code has been updated to demonstrate that the mainform may be resize, the contents of the ListView controls can be sorted by clicking the column headers and column headers re-ordered by dragging. All with no thread safety issues. If you run the code and cause a thread safety related error to be raised please email me.
|
|
|
|
 |
|
 |
Thank you very much for updating the article. Not everyone would have done that after the many discouraging replies.
I still wouldn't use the technique in production code. The simplest reason is maintenance: any update, even a patch, for the .NET runtime may "legally" break your code.
A slightly deeper reason is that it's not a general technique. Any change to the UI layer may require a different emchanism to work reliably. The third reason is that I know how hard threading issues are to debug, and they might occur only on the computers of your most important customer - and as soon as you add some traces or - behold - run it under the debugger, they are gone.
But as Jared said, everything has it's place. For a quick hack, and understanding it's limitations, why not? I just want to avoid some novice programmer who does not understand the details says "cool - that's so easy! And it got a good rating, so it must be right!"
Why is this working most of the time, and why sometimes not
As said, there is some state of the control that may be altered in a non-threadsafe manner. To understand why this usually doesn't happen, we have to look at some details that are not really documented, and may change with every minor update. And I might be wrong, much of this is derived from guided guessing.
We have to look separately at the Win32 control (ListView Control) and the .NET framework class that wraps it. Since the Win32 List View state can only be modified through windows messages (Send/PostMessage), which always execute on the thread that created the control, these changes are threadsafe, though only on a per-message basis*. State that is held directly in the .NET class, however, is not threadsafe.
Many of the functions and properties exposed by the wrapper classes sirectly manipulate the Win32 control's state. Most controls can largely be wrapped without implementing any state in the .NET wrapper at all. As long as you are hitting only these properties, usually nothing will go wrong.
Even if only one thread modifies .NET state, you are fine. Only when both threads start to modify state that is implemented in the .NET wrapper class - like cell color for the ListView control - you will get into trouble.
To be frank, if there would be any errors due to race conditions in your sample, one wouldn't be able to see them with the rapid and seemingly-random updates.
Most multithreaded Win32 GUI programming relied on the fact that SendMessage/PostMessage do an implicit thread synchronization. MFC strongly suffered from the problem, and went at great lengths (and quite some hacks) to avoid any state inside the MFC wrapper class.
So there are two general solutions for the library designer: don't allow any state in the wrapper class - which pretty much prohibits almost every extension to the control - or make the wrapper class state thread safe, which opens some nasty worm cans.
Alternatives
There's no panacea, at least none that doesn't need some fine tuning to the situation, data and control at hand (so my "not a general solution" argument above is a bit weak).
The general approach that comes to (my) mind: update the main thread in chunks. Either use BeginInvoke on a block of items, or use a thread safe deque and an update/notification mechanism that fits the scenario. For listview controls, "Virtual Mode" offers can be a boon, though you have to make sure the data store does not become a lock hotspot.
*) consider the WM_SETTEXT, WM_GETTEXTLENGTH and WM_GETTEXT messages to get and set control text. Imagine the following scenario:
Thread 1 sets control text to "Hamlet".
Thread 2 wants to read the control text. For this it first gets the text length (6 characters),
Thread 2 allocates a buffer for 6 chars + one terminating zero
Thread 1 sets control text to "Ophelia"
Thread 2 sends WM_GETTEXT to get the text into the buffer just allocated.
In the best case, thread 2 just gets "Opheli". Many similar APIs have a good chance for a buffer overrun if the programmer isn't extra careful.
Everything is still fine if thread 2 is the thread that created the control, and is currently processing a message (because then, Thread one couldn't "inject" a message). If thread 2 is your worker thread, however...
|
|
|
|
 |
|
 |
Unless I am missing something the grand statement of this article is to turn off the built-in checking for cross-thread GUI calls?
If that is the case then you are essentially turning off the safety feature in .NET and in doing so your code is broken.
|
|
|
|
 |
|
 |
Hi Nathan,
The cross thread checks are turned off in order to be able to use this technique. Provided you follow the guidelines in the article this technique is perfectly thread safe. I've updated the sample code so that the mainform may be resize, the contents of the ListView controls can be sorted by clicking the column headers and column headers re-ordered by draging. If you can run the code and cause it to fail through a thread safety issue I would like to hear about it. As I say in the article, this technique is only appropriate in a very few instances but when those conditions are met it can be useful.
|
|
|
|
 |
|
 |
I'm glad you documented this technique.
We're always trying to figure out a way of keeping the UI as responsive as possible. I've tried a similar approach, but checking InvokeRequired.
Now I have something else to try.
Good job.
|
|
|
|
 |