I recently updated an application from VS2003 to VS2008 and I knew I would be dealing with a host of "Cross-thread operation not valid: Control 'myControl' accessed from a thread other than the thread it was created on" I am handling this in what I beleive is the correct way (see code sample below). I am running into numerous controls that are going to need a similar fix. Not wanting to have similar code for every label, textbox etc.. that are being accessed by a non UI thread. What are the ramifications of just setting the CheckForIllegalCrossThreadCalls = false for the entire app?
I found a CodeProject article with various workarounds and a warning at the bottom to NOT set the property. I am looking for other opinions/experiences on this issue.
private void ShowStatus(string szStatus)
{
try
{
if (this.statusBar1.InvokeRequired) { BeginInvoke(new MethodInvoker(delegate() { ShowStatus(szStatus); })); }
else { statusBar1.Panels[0].Text = szStatus; }
}
catch (Exception ex)
{
LogStatus.WriteErrorLog(ex, "Error", "frmMNI.ShowStatus()");
}
}
I found another article with some possible solutions SO Question 2367718
When you're not debugging, you'll still have problems.
From the documentation of Control.CheckForIllegalCrossThreadCalls:
Note that illegal cross-thread calls will always raise an exception when an application is started outside the debugger.
You'll need to correct the problems.
That being said, you mentioned:
Not wanting to have similar code for every label, textbox etc.. that are being accessed by a non UI thread.
I would reconsider this stance. You should try to move the logic running on a separate thread into separate methods or classes, which will in turn make marshaling the calls back into the UI much simpler. Over time, this will make your code much more reliable and maintainable.
Note that you can use Control.Invoke to marshal a whole set of calls to the UI in one call, too, instead of doing each single set operation individually. There really shouldn't be that many of them, when you finish.
Edit:
For example, it sounds like you're loading the data. Say you have (on your background thread), your data loading method:
var myData = LoadData();
this.Invoke( new Action( () =>
{
// Just set all of your data in one shot here...
this.textBox1.Text = myData.FirstName;
this.textBox2.Text = myData.LastName;
this.textBox3.Text = myData.NumberOfSales.ToString();
}));
The remarks section of the documentation for CheckForIllegalCrossThreadCalls makes it pretty clear that doing so is not a good idea
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.
Note that illegal cross-thread calls
will always raise an exception when an
application is started outside the
debugger.
Related
EDIT:
please see question history, for unchanged question in order not to invalidate comments.
I am clicking button that executes certain codes and it creates a thread (System.Threading.Thread). When I reclick button which starts process it hangs and freezes ui. What could be the reason?
public partial class ucLoader : UserControl
{
//lock object for whole instance of class ucLoader
private object lockUcLoader = new object();
//bringing info from ui
private void btnBringInfo_Click(object sender, EventArgs e)
{
lock (lockUcLoader)
{
btnBringInfo_PerformClick(false);
}
}
//using this method because it could be called when even button not visible
internal void btnBringInfo_PerformClick(bool calledFromBandInit)
{
lock (lockUcLoader) //HANGS HERE when called multiple times and ui freeze as well
//by the way I am using (repetitive) lock, because this method also called independently from btnBringInfo_Click
{
//...
this.btnLoad_PerformClick();
}
}
//Another button perform click that could be triggered elsewhere when even button not visible
private void btnLoad_PerformClick()
{
lock (lockUcLoader) //I am using (repetitive) lock, because this method also called independently from btnBringInfo_PerformClick
{
//...
Run();
}
}
//method for creating thread which System.Threading.Thread
private void Run()
{
lock (lockUcLoader) //Maybe this lock is NOT REQUIRED, as it is called by only btnLoad_PerformClick(), could you please confirm?
{
//some code that thread can be killed when available, you can ingore this two lines as they are irrelevant to subject, I think
Source = new CancellationTokenSource();
Token = Source.Token;
var shell = new WindowsShell();
Thread = new Thread((object o) =>
{
//...
var tokenInThread = (CancellationToken)o;
exitCode =TaskExtractBatchFiles(cls, shell, exitCode);
using (var logEnt = new logEntities())
{
//Do some db operation
//...
this.Invoke((MethodInvoker)delegate
{
//do some ui update operation
//...
});
}
}
Thread.Start(Token);
}
}
public void Progress(string message)
{
Invoke((MethodInvoker)delegate //ATTENTION HERE see below picture Wait occurs here
{
if (message != null && message.Trim() != string.Empty)
{
this.txtStatus.AppendText(message + Environment.NewLine);
}
});
}
}
In order to avoid get closed question, what my question is how can I prevent
below method can be accesses with out lock from background thread and ui thread
public void Progress(string message)
{
Invoke((MethodInvoker)delegate //ATTENTION HERE see below picture Wait occurs here
{
if (message != null && message.Trim() != string.Empty)
{
this.txtStatus.AppendText(message + Environment.NewLine);
}
});
}
Invoke((MethodInvoker)delegate ...
Whenever you use the lock statement in your code then you always run the risk of inducing deadlock. One of the classic threading bugs. You generally need at least two locks to get there, acquiring them in the wrong order. And yes, there are two in your program. One you declared yourself. And one you cannot see because it is buried inside the plumbing that makes Control.Invoke() work. Not being able to see a lock is what makes deadlock a difficult problem to debug.
You can reason it out, the lock inside Control.Invoke is necessary to ensure that the worker thread is blocked until the UI thread executed the delegate target. Probably also helps to reason out why the program deadlocked. You started the worker thread, it acquired the lockUcLoader lock and starts doing its job, calling Control.Invoke while doing so. Now you click the button before the worker is done, it necessarily blocks. But that makes the UI thread go catatonic and no longer capable of executing the Control.Invoke code. So the worker thread hangs on the Invoke call and it won't release the lock. And the UI thread hangs forever on the lock since the worker can't complete, deadlock city.
Control.Invoke dates from .NET 1.0, a version of the framework that has several serious design mistakes in code related to threading. While meant to be helpful, they just set death-traps for programmers to blunder into. What is unique about Control.Invoke is that it is never correct to use it.
Distinguish Control.Invoke and Control.BeginInvoke. You only ever need Invoke when you need its return value. Note how you don't, using BeginInvoke instead is good enough and instantly solves the deadlock. You'd consider Invoke to obtain a value from the UI so you can use it in the worker thread. But that induces other major threading issue, a threading race bug, the worker has no idea what state the UI is in. Say, the user might be busy interacting with it, typing a new value. You can't know what value you obtain, it will easily be the stale old value. Inevitably producing a mismatch between the UI and the work being done. The only way to avoid that mishap is to prevent the user from typing a new value, easily done with Enable = false. But now it no longer makes sense to use Invoke, you might as well pass the value when you start the thread.
So using BeginInvoke is already good enough to solve the problem. But that is not where you should stop. There is no point to those locks in the Click event handlers, all they do is make the UI unresponsive, greatly confuzzling the user. What you must do instead is set the Enable properties of those buttons to false. Set them back to true when the worker is done. Now it can't go wrong anymore, you don't need the locks and the user gets good feedback.
There is another serious problem you haven't run into yet but you must address. A UserControl has no control over its lifetime, it gets disposed when the user closes the form on which it is hosted. But that is completely out of sync with the worker thread execution, it keeps calling BeginInvoke even though the control is dead as a doornail. That will make your program bomb, hopefully on an ObjectDisposedException. A threading race bug that a lock cannot solve. The form has to help, it must actively prevent the user from closing it. Some notes about this bug in this Q+A.
For completeness I should mention the third most common threading bug that code like this is likely to suffer from. It doesn't have an official name, I call it a "firehose bug". It occurs when the worker thread calls BeginInvoke too often, giving the UI thread too much work to do. Happens easily, calling it more than about thousand times per second tends to be enough. The UI thread starts burning 100% core, trying to keep up with the invoke requests and never being able to catch up. Easy to see, it stops painting itself and responding to input, duties that are performed with a lower priority. That needs to be fixed the logical way, updating UI more than 25 times per second just produces a blur that the human eye can't observe and is therefore pointless.
I used a WebBrowser control to navigate to the login page for a WordPress blog.
The page loads fine but whenever I try to access the WebBrowser from a thread. I get a specific cast is not valid exception. Also when debugging, everything freezes up for around 5 seconds. When debugging and I try to access the control. I get timed out errors on all of the member variables.
//in constructor of main form
Thread.CurrentThread.ApartmentState = ApartmentState.STA;
this.CheckForIllegalCrossThreadCalls = false;
mainThreadHandle = new Thread(mainThread);
mainThreadHandle.Start();
private void mainThread()
{
wbMain.Navigate("http://example.com/");
//navigating is set to false in the document complete event.
navigating = true;
while (navigating == true)
Thread.Sleep(5000);
try
{
//Where I get the issues
MessageBox.Show(wbMain.DocumentText);
}
catch (Exception e)
{
}
Thread.Sleep(1000);
}
WebBrowser is a COM component under the hood. An apartment threaded one, COM takes care of calling its methods in a thread-safe way. Your Navigate() call works for that reason, it is actually executed on the UI thread. What doesn't work is the DocumentText property, it is implemented in the .NET wrapper and they somewhat fumbled the code. It bombs when the COM interop support in the CLR notices that a thread in the MTA tries to access a property of a component that lives on an STA.
Your call to SetApartmentState() isn't correct. It is made on the wrong thread, the UI thread already is STA. Also the reason it doesn't bomb, you cannot change the apartment state of a thread after it is started. You must call it on the Thread object you created. It still doesn't solve your problem, two STA threads are not compatible.
Two basic ways to solve your problem. The first one is that you create the WebBrowser object itself on a separate STA thread. The code in this answer shows you how to do that.
The browser you create that way cannot also be visible on your form. Which is the second way, marshal the call yourself with Control.Invoke(). Doing this is however pretty pointless, all of your code executes on the UI thread anyway, you get no concurrency. There is no free lunch here. Running it on a thread only gives you headaches. If you need time to process the document text then run that code on a separate thread.
Why not use event model when work with WebBrowser control?
Navigating, Navigated, ProgressChanged, DocumentCompleted
Code below runs OK. I wonder if it is really correct?
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
Parallel.ForEach(openFileDialog.FileNames, currentFile =>
{
try
{
StreamReader FileReader = new StreamReader(currentFile);
do
{
URLtextBox.Invoke(new MethodInvoker(delegate
{
URLtextBox.Text += SelectURLfromString(FileReader.ReadLine());
}));
}
while (FileReader.Peek() != -1);
FileReader.Close();
}
catch (System.Security.SecurityException ex)
{
...
}
catch (Exception ex)
{
...
}
});
}
Otherwise I get either "Cross-thread operation not valid. Control 'URLtextBox' accessed from another thread" or stuck application.
The code is correct - you need to use Invoke to refresh controls from outside the GUI thread. However, you are excuting the SelectURLfromString(FileReader.ReadLine()); method in the GUI thread as well, you should replace that by
string url = SelectURLfromString(FileReader.ReadLine());
URLtextBox.Invoke(new MethodInvoker(delegate
{
URLtextBox.Text += url;
}));
to minimize the work in the GUI thread to a minimum.
You cannot update UI controls from worker threads safely, unless you marshall onto the UI thread.
Take a look at TaskScheduler.FromCurrentSynchronizationContext
How to: Schedule Work on a Specified Synchronization Context
The code is correct, you need the Invoke call so that the control is updated in the GUI thread.
However, there are some other things that doesn't really make sense in the code:
You are doing parallel operations that is using a resource that is not parallel. Your threads will be fighting for attention from the disk, which is clearly the bottle neck due to it's relatively low speed.
You will read lines from several files, and dump them intermixed in a textbox. That might be all right in this specific situation, but generally it gives an unpredictable result.
You are using the += operation to concatenate strings, a method that is notorious for it's bad scalability. It might not be a big problem in this case though, as the disk bottle neck is probably a lot worse.
The Invoke is necessary because controls are bound to the thread that created their associated User32 window (often called an HWND). That said, you could probably optimize a little by reading and processing the contents of the file outside of the Invoke's delegate.
I'm using the following method to show a modeless Message Box.
public void ShowMessageBox(string Message)
{
var thread = new Thread(
() =>
{
MessageBox.Show(Message);
});
thread.Start();
}
The "() => {...}" is something I've never seen before. What is the name for this code pattern?
Also, thread.Start starts the thread, and it automatically closes once the "()=>{...}" method completes (when the Message Box is OK'ed), right? If so, can you please point me to some official documentation saying that the thread closes automatically?
Thanks!
It's the lambda operator, and read as "goes to". MSDN has a good intro: Lambda Expressions (C# Programming Guide)
One concern with your example is that you're spinning up a new thread to update the UI, the UI is intrinsically single-threaded, so background updates are generally the wrong thing to do (unless you're manually/explicitly checking InvokeRequired and calling Invoke() as needed.
Regarding the UI threading...
In WinForms every Form or Control is created on a particular thread (the "UI Thread"), and you can think of that thread as owning that control (not exactly correct, but a good way to conceptualize it). Updating the UI from that thread is safe, updating the UI from another thread runs the risk of collisions and corruption and all the usual risks of parallel/async programming.
...So... how do you safely update the UI from a background thread without blocking the UI? In short--you can't--the best you can do is block it for the bare minimum required to update the UI. This is where InvokeRequired and Invoke() come in...
Here's a sample: you should be able to drop this into the code-behind of a new form with a button and textbox.
To use:
Try commenting out either the call to SetTextAsyncSafe() or SetTextAsyncSafe() -- running both could confuse you since they won't necessarily execute in the order they're called (they're running async, remember?).
Then set a breakpoint on SetText(). You should see the "safe" call will actually call the method twice--the first call will detect InvokeRequired and will call the method a 2nd time for the correct thread by Invoke()'ing to it.
You should see an Exception thrown when SetTextAsyncUnsafe() actually gets to the textBox1.Text = value; statements. The exception will be an InvalidOperationException with a message stating "Cross-thread operation not valid" -- you can google this term for more details.
The code:
private void button1_Click(object sender, EventArgs e)
{
SetTextAsyncSafe("This update was made from the UI Thread by using Invoke()");
SetTextAsyncUnsafe("This update was made directly from the background thread and can cause problems");
}
private void SetTextAsyncUnsafe(string value)
{
new Thread(() => SetText(value, false)).Start();
}
private void SetTextAsyncSafe(string value)
{
new Thread(() => SetText(value, true)).Start();
}
private void SetText(string value, bool checkInvokeRequired)
{
if (checkInvokeRequired)
{
if (InvokeRequired)
{
Invoke(new Action(() => SetText(value, checkInvokeRequired)));
return; // early exit
}
}
textBox1.Text = value;
}
That is a Lambda. In this case, you're using it to create a new anonymous method that will be run when the new Thread is started.
It's the (near) equivalent of:
public void ShowMessageBox(string Message)
{
var thread = new Thread(ShowBox);
thread.Start(Message);
}
public void ShowBox(object message)
{
MessageBox.Show(message.ToString());
}
This is called a Lambda Expression. You can read more here.
Lambda expression, C# version 3 feature.
Don't use this code. A message box needs a parent window, something it can make sure to be on top of. It can normally find a parent by itself by iterating the windows that were created on the same thread. Not in this case though, there are no other windows, it has to pick the desktop window as the parent.
That will go wrong badly when the user is working in an app window or switches focus to another app, the message box disappears behind the foreground window. There is no obvious way for the user to tell that it is there, she'll just loses sight of it. It could be hours, if not days, before she finds it back. That thread is meanwhile consuming resources badly, you would probably never consider it if you knew that this message box requires a megabyte of memory. In extreme cases, you'll crash the program with OOM.
The common alternative in Windows UI programming is a balloon tooltip provided by a NotifyIcon. Or your own form with the TopMost property set to True so it cannot easily get lost. Also allows you to control the position, important for "non-modal" notifications that should not get in the way. Set that form's ShowWithoutActivation property to true in the form constructor so it doesn't steal the focus.
Its a statement lambda.
Yes, thread is active as long as this anonymous method is running. Since after MessageBox.Show() there is no other statements, thread will exit, and this must be true... if you are in doubt add this before start:
thread.Name = "LALALA";
And then debug your app. When the message box apear, pause execution, go to Threads View and you will see LALALA running. Click OK and pause again, there should be no "LALALA"... =)
I was testing a program I am writing and I got this error message: Cross-thread operation not valid: Control 'lblStatus' accessed from a thread other than the thread it was created on
The code is a bit massive and I am not sure which part is causing this error to post a smaller segment. However here is some info that might be of use.
I am not using any "threading" explicitly. I am guessing something else is automatically creating multiple threads - I am using a Wii remote hardware access library and I am doing graphics manipulation.
The stack trace indicates, that a call is made to an on change event handler, which calls a function inside which lblStatus is tried to be modified - but fails.
I was wondering how do you debug these types of errors. I am using Visual Studio 2008.
EDIT
One thing I want to clarify, I do not know how this different thread even came up. How would I even find this? the source of the different thread.
public void SetStatus(string msg)
{
if (lblStatus.InvokeRequired)
lblStatus.Invoke(new MethodInvoker(delegate
{
lblStatus.Text = msg;
}));
else
lblStatus.Text = msg;
}
This will get your label text updated.
For a BeginInvoke, this is the way I know (I know there are more elegant implementations) - but I haven't tried this in a multi-threaded app yet:
Action<string> setStatus= target.AppendText;
void OnSomeEvent (object sender, EventArgs e)
{
IAsyncRes iares = setStatus.BeginInvoke("status message", null, null);
setStatus.EndInvoke(iares);
}
public void SetStatus(string msg)
{ lblStatus.Text = msg; }
For the different methods of synchronizing to the control thread, SnOrfus references an excellent link. My example above on BeginInvoke is not correct for synchronizing to the control's thread.
As far as what is causing the thread: About WiimoteChanged event
You can't access visual elements from any thread other than the main thread. You need to use Dispatcher.Invoke() to call a delegate which updates the visual elements.
Example:
this.Dispatcher.Invoke((Action)(() => lblStatus.Content = "Hello"));
or (without lambda):
this.Dispatcher.Invoke((Action)delegate() { lblStatus.Content = "Hello"; });
You need to combine the use of Delegates with your threads..
Assign your method to a delegate and then invoke the delegate through thread.
One thing I want to clarify, I do not know how this different thread even came up
If your app is not multithreaded, the problem could be caused by the Visual Studio hosting process. The Visual Studio hosting process starts up a main thread and then creates a new UI thread that the application is run on. In some circumstances this can lead to a cross-thread exception, for example when accessing Form.ActiveForm before calling Application.Run.
If this is your problem, you can uncheck "Enable the Visual Studio hosting process" in the Debug tab of Project Properties.