Can I read the state of controls in non-UI threads - c#

Can I read the state of controls (not change it) in non-UI threads like this
if (!string.IsNullOrWhiteSpace(notifyIcon.BalloonTipText))
{
// ...
}
Is it safe?

See this artice from Visual Studio Magazine about Multithreading in WinForms.
As explained on page 2, one of the problems of accessing UI controls from a thread other than the thread that created the control is that that thread doesn't have a message pump (or at least not necessarily has one). "Reading" UI controls state is done through window messages (for example, WM_GETTEXT) just as writing to them, and this is wrong in the same way.

There is a property named "InvokedRequired" in every control. Check for this property, if it is false you can call method in regular way, otherwise call invoke method with the help of delegate from the control you are trying to access.

Related

Fundamental question about synchronizing with the UI thread in Winforms using c#

I know Winforms has only one UI thread, and if you need to update it while you are on another thread, you should use invoke method.
My questions is if what you are doing does not change the look of any control, would that still be accessing the UI thread?
For example, my form1 has a logger which write messages to a local file which users don't see at all. If I use the logger in another thread, say write a new message, would this be considered accessing the UI thread? Yes, logger is initialized in form1.cs, but it has no visual representation on the form at all. Do I still need to use invoke?
Also, if I have a custom control which extends a textbox. The custom control has a property called initialized. Changing this property has no effect on the look of the control whatsoever. Then, if I update this property from another thread, do I need to use the invoke method?
If only one thread uses the logger instance, no need to synchronize, else if several threads can write to it, you need to synchronize, not with the UI thread but between threads, using lock or monitor for example.
It is the same thing for the last question.
Simple experiment seems to say if you are not changing the looks of a control, then you are thread safe.
You can see in the screenshot, when I set a member of my form call blnButtonDown(a bool), it was OK. When I changed the name of a textbox of my form, it was still OK. It was only when I changed the textbox's text, which changes the look of the form, that I got an exception thrown.
So my tentative conclusion is that if you are changing a form/control from another thread, you don't have to use invoke if your changes do not change the look of the form/control.

Legacy code performance issue - Create UIControl on second thread, not only alter it

I inherited a winforms app. It uses a third-part-closed control that renders documents and photos... It has only sync methods for opening a document. The problem is that my clients are dealing with really big documents (in the area of 2GB!!!) and opening these docs really "block" the UI thread... which is bad...
Common sense would make you think "Just off-load it to a background thread" but the question is "HOW"! See, to alter the control (because calling "Open" causes it to be altered) I need to Invoke it, and that causes the code to run o UI thread again... locking it up...
So I turned the table upside down. What if instead of creating the control on the main thread and passing it to a background thread for processing, I could create the control on the background thread, load it up (avoiding this way the cross-thread exception) and, when done, feed it to the main thread?!?
Right now what I need is to know how to definitively handle a control to another thread, and not only temporally...
I'm not sure if this is possible but you could try to:
create a new form on a secondary thread (this form will host your fancy control)
load the document from this secondary UI. It will be blocked but you can hide it and only display a
loading message on the main UI.
when the job is finished transfer the 'work' to main UI and main thread.
It's just an idea.
What you are asking to do is impossible. A Winforms control's thread affinity is determined when that control is created, and it cannot be changed.
The best solution is to not use that control. I doubt there's anything it does that cannot be implemented correctly and competently by someone else.
If you are okay running a completely different window in a second STA thread, then that would be the next best thing. That particular window will still be frozen while the document loads, but at least your main UI would still be okay. Note that you should not try to mix and match controls from different threads in the same window; that will lead to all kinds of headaches.
Finally, as a complete hack, you might consider going ahead and calling this Open() method in a background thread in spite of the control being owned by the main UI thread. On the admittedly shaky assumption that the only time that control will actually attempt to access the UI component itself would be at the very end of the Open() method operation, you can go ahead and catch the InvalidOperationException that is thrown, and use that as your signal that the document loading has completed. Then just invalidate the control in the main UI thread.
I'd give the odds of this last suggestion working no better than 50/50. It will depend on what the control actually does with the loaded data, and if it's some kind of composite control where it's relying on actually taking the result of its loading and copying that to a control as part of the Open() method, that part might fail and the control would not wind up properly initialized.

Multi-thread debugging with VisualStudio and WebBrowser Windows.Forms controls

We're creating a few threads in a Windows.Forms solution.
We've also got a BrowserControl (yup, it's OAUTH) and of course I'm finding issues with debugging -
Cross-thread operation not valid: Control 'xxForm' accessed from a
thread other than the thread it was created on
And yet I am calling 'correctly'
xxForm.Show()
by ensuring it's wrapped in an invoke call
.Invoke(new MethodInvoker())
and still I get the thread issue. I can do everything else (Focus, BringToFront) it's the Show that errors.
Moreover, the GUI never gets the browser response and shows. In the VS debugger I get the above threading erropr (apartment state of background thread == MTA). When run as an EXE the same code has a threading aparement of STA and the GUI will never show when debugging, but if I run the EXE directly, there's no threading issue, the browser control just never responds to input.
Spo the questions are:
Why the different behjaviour in VS / EXE?
How do I debug in VS?
Does the BrowserControl play ok with multiple threads?
Update
YES I KNOW there's no code - it's too long. I'll see what I can do.
Also please read the question before posting an answer. I am invoking the background thread on the UI thread. That's not the issue. This has NO EFFECT on the result. I'll down vote answers that recommend that.
Also some hope - I've searched for many hours. It's linked the the message pump. Unless the message pump is running the DocumentCompleted event isn't received.
Update 2
Best I manage is pseudo code:
Program:
startWorkQueue
LoadForm (don't show)
Thread1:
addToQueue
Thread2:
readFromQueue
ShowForm (on occasion)
Now the ShowForm method is on a Windows.Form control.
Within this any call is made via an Invoke, so that despite the 2nd thread making the call the ShowForm, the actual .Show() etc etc calls are on the UI thread.
So what am I missing? I just don't understand why there's a cross thread exception....
Ok I've found out what was going on and I'll post here for reference.
I hope it helps someone in the future.
We had some different constraint to the vanilla Forms applications - our Forms were created on the Main thread but had Show() called on a different thread. We also don't have a starting UI - we sit in the background and sometimes react to events with UI prompts.
As such a number of issues hit us. Rather than list them all I'll detail our takeaways:
Application.Run
If decide NOT to Show a Form at the start of your application… then you'll need to consider threads carefully (see Form.Show notes)
Form.Show
This does all resource allocation etc, NOT the Load/ctor
Performs handle creation / resource allocation
Handle creation
Hwnd
If we attempt to access certain Form properties before it has Show(n) then you'll need to create a handle manually (or an Exception is created)
• As easy as if (!IsHandleCreated) { CreateHandle(); }
HOWEVER
This create the control/form
Therefore the control/form is created on the Thread that calls the Handle create
This must be the same UI thread (STA) as the Main function
• Otherwise much weirdness occurs
SO
You aren't restricted by what to do with Application.Run
You can access properties in .Show (but you may need to create a handle first)
You can call Show from a different thread, but ensure the handle is ONLY created on the main thread
I hope it helps, I can provide more details on our specific problems if need be.
Various aids that helped included displaying ManagedThread AND ProcessId in the log and scouring MSDN.
Since the code has not been pasted, I would like to remind you that in WinForms, the UI elements should be accessed only on the UI thread. Any other thread apart from UI thread should not be updating the UI elements directly.

UI freezing problem in c# 2.0

Sometimes I saw that when I call a method from my form to do something that my UI freezes. How to solve this problem? If I call that method in separate thread then problem will be solved?
If I call method in separate thread like the code below
new System.Threading.Thread(delegate()
{
HeavyMethod();
}).Start();
does this solve my problem or is there any better solution?
Call the method on a Background Worker would be the best solution.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Doing that you can control when things get updated (using the Report Progress Feature) and allow you to cancel the work.
Also, make sure that whatever resources you manipulate in the backgroundWorker1.RunWorkerAsync(); are properly shared. You can get into what is called "Race Conditions" which causes your output to be non-determanistic (e.g. you won't get the same results every time you run the method)
For a good walk through on Multithreading and shared resources, see this link:
http://www.c-sharpcorner.com/uploadfile/mgold/multithreadingintro10062005000439am/multithreadingintro.aspx?articleid=920ecafc-e83b-4a9c-a64d-0b39ad885705
If you are calling your method in response to an event, then by default the method will be running on the GUI thread (the thread that the runtime uses to handle all user events). If that method is huge and/or heavy, then it will "freeze" the UI as you describe.
Making it run on a separate thread is a viable solution for many of these cases.
There are cases, however, when you'll actually want the UI to "block" (for example, if you are updating a lot of controls, you don't want the user to mess with them in the meanwhile). For such cases, the sanest approach is to pop up a modal "wait" dialog.
Since it is C# 2.0, I suppose it is WinForms. Don't hold up the UI thread with CPU-bound code.
You can spawn a new thread to run your CPU-bound code, but you have to be careful not to access WinForms controls, especially not to update control properties. Many WinForms controls can only be accessed/updated from the UI thread. Check the InvokeRequired field to see if you need to marshal (i.e. use Invoke) the call from another thread back to the UI thread.
Also consider using the ThreadPool instead of creating a new thread.
That is correct, If you move the heavy processing off of the UI Thread then it should free up the UI to redraw. For what you want to do your implementation should work just fine. Although ThreadPooling or BackgroundWorker would be the suggested implementations (http://msdn.microsoft.com/en-us/library/system.threading.threadpool(v=VS.80).aspx), (http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx).

Making thread-safe calls to windows forms controls

The MSDN article: How to: Make Thread-Safe Calls to Windows Forms Controls
says we should use async delegate to make the call. But why does the async delegate make the call safe?
Windows controls use the Component Object Model (COM) single-threaded apartment (STA) model because those underlying controls are apartment-threaded. Furthermore, many of the controls use the message pump for many operations. This model says that all function calls to each control must be on the same thread that created the control. Invoke (and BeginInvoke and EndInvoke) marshals method calls to the proper thread.
From Bill Wagner's More Effective C#. Item 16. Understand Cross-Thread Calls in Windows Forms and WPF
You'll call control.BeginInvoke() or control.Invoke() and that method will take care of inserting your delegate in the GUI dispatch thread safely, so a bit later on your delegate will be processed and executed in the GUI thread and not the thread you'r in
The bottom line is this: You shouldn't update the UI Controls from a thread other than the one on which the control was created (UI / Main Thread). Otherwise you may see some unpredictable behavior.
If you need to update the UI from a worker thread (other than the main thread) you need to switch back to the UI Thread before updating the UI.
The article suggests using
IsInvokeRequired (which returns true if the current thread is not the one in which UI was created.) and Invoke(delegate) which runs the delegate on the correct/UI thread. This is useful when you want to update the UI in between the async process. e.g. update progress on the UI.
BackgroundWorker which executes registered handlers to its DoWork event async. on a worker thread and runs the registered handlers to its RunWorkerCompleted event on the calling thread. This is ideal if you want to update the UI with something after the async task has completed. e.g. post a Done indication on the UI
Because, windows forms controls are designed in that way, so they can be accessed only from within he thread, which owes them. And the async delegate, when used correctly, makes the call safe.
The actual answer to the given question is contained in the second paragraph of the given MSDN article =)
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
You should check whether you can access control immediately, without indirection ( checking the InvokeRequired property ), if you can't, you should access it asynchronously ( very simplified explanation: the system will wait until it can safely access a control )

Categories