I'm trying to load loadingForm like below code. But it doesn't work, the loadingForm doesn't disappear, the event RunWorkerCompleted doesn't get called.
And also, I need to call loadingForm and backgroundWorker multiple times, so how do I completely dispose the loadingForm and the backgroundWorker after each call?
I think that there're many things wrong in my code but I don't know how to fix it. Could you show me how to solve my problem and point out where I need to fix? Thanks a lot in advance.
public partial class loginForm : Form
{
//....
private loadingForm lf;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
lf.Show();
While (backgroundWorker1.isBusy)
Application.DoEvents();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, DoWorkEventArgs e)
{
lf.Close();
}
private void connect()
{
//....
Thread mainThread = new Thread(ThreadStart(listentoServer));
mainThread.Start();
}
private void listentoServer()
{
//....
lf = new loadingForm();
backgroundWorker1.RunWorkerAsync();
//....
backgroundWorker1.CancelAsync();
//....
}
}
There's a lot of things wrong with your code. If you can, try to take a step back and describe what exactly you want to do.
BackgroundWorker uses the Event-based Asynchronous Pattern (EAP). As such, it requires a thread context in which to live. UI threads satisfy this requirement, but manually-created Thread instances do not (unless you install one or make the instance a secondary UI thread).
Similarly, UI components bind to a particular thread. They require an STA thread that does message pumping (e.g., Application.DoEvents).
It looks to me like you're creating a manual Thread and then creating UI components from that thread (so you know that the thread should be STA and include a message pumping loop, neither of which are in your code). Then that thread starts a BGW which does message pumping.
It's not clear what you're trying to accomplish here - maybe displaying a dialog in a separate thread?
Multiple UI threads in a WinForms app is not an officially supported scenario AFAIK, though some people have gotten it working. I've never seen a need for it, though.
According to what you have shown (which is admittedly incomplete, so this may not be the problem), you are not hooking up your event to the backgroundWorker_DoWork and backgroundWorker_RunWorkerCompleted event handlers. Somewhere (after you instantiate your backgroundWorker), you should have this:
backgroundWorker.DoWork += new EventHandler(backgroundWorker_DoWork);
backgroundWorker.RunWorkerCompleted += new EventHandler(backgroundWorker_RunWorkerCompleted);
As a disclaimer, this was written by hand, so the event names or EventHandler types may be incorrect.
i really don't know how to fix your code definitively, or if your code even works the way you have it, i can only give you the following guidance.
use CancellationPending property of background worker, not the IsBusy property
when working with windows forms and threaded code, always use the Invoke/BeginInvoke methods to make sure you marshal your call back to the thread that the control originated from.
Related
I'm changing the Text of a button from a BackgroundWorker and it works. I thought that was supposed to throw an exception. Why doesn't it?
Why don't I get a Cross-thread operation not valid: ... accessed from a thread other than the thread it was created on.?
EDIT: Thanks everyone.
Perhaps the reason was that there was a: Thread.Sleep(1000); on the UI.
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync();
Thread.Sleep(1000);
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
button1.Text = "a";
}
However, I noticed that this following code runs fine as well, despite affecting the UI (indirectly).
public partial class Form1 : Form
{
int i;
public Form1()
{
InitializeComponent();
i = 1;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync();
for (int j = 0; j < 100000000; j++) ;
button1.Text = i.ToString();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
i = 2;
}
}
Why?
The callback function invoked by the background worker once work is completed (BackgroundWorker.RunWorkerCompleted) is - for your convenience - using the UI thread dispatcher already.
Edit:
#ispiro: It's not guaranteed that the code in your second example will always work - you still have a cross-thread update of the variable i so you should declare it volatile to make sure it is always updated correctly.
The reason the first code should not work is that the .NET framework helps you out detecting this cross-thread access. As #Greg D pointed out this can be disabled though (which definitely is a no-no). For more information check this MSDN page:
The .NET Framework helps you detect when you are accessing your
controls in a manner that is not thread safe. When you are running
your application in the debugger, and a thread other than the one
which created a control tries to call that control, the debugger
raises an InvalidOperationException with the message, "Control control
name accessed from a thread other than the thread it was created on."
This exception occurs reliably during debugging and, under some
circumstances, at run time. You might see this exception when you
debug applications that you wrote with the .NET Framework prior to the
.NET Framework version 2.0. You are strongly advised to fix this
problem when you see it, but you can disable it by setting the
CheckForIllegalCrossThreadCalls property to false. This causes your
control to run like it would run under Visual Studio .NET 2003 and the
.NET Framework 1.1.
Because your callback function for the RunWorkerCompleted event is called (invoked) on the UI thread for convenience. Realize though that this is not true for every event, obviously the DoWork callback runs on a separate thread.
There are a few possibilities:
1) Your program has disabled cross-thread checks. This is a sadly common hack that software uses when people don't understand the threading rules around a UI thread.
2) Your program is modifying the UI via the BackgroundWorker's Progress or Completed events. These events are marshalled to the synchronization context of the thread that created the BackgroundWorker. If the BackgroundWorker is being used in its classic context as a WinForms designer component, you're golden. The events are getting marshalled to the UI thread for you, so you don't have to do such nonsense yourself.
This question is sort of misleading as BackgroundWorder only provides certain guarantees. The following is a "Note" from the BGW documentation:
You must be careful not to [do not] manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.
The RunWorkerCompleted and ProgressChanged events are posted to the thread that created the BGW*. The same guarantee does not hold for the DoWork event, however: do not access the UI from within it :)
In essence, the code in RunWorkerCompleted and ProgressChanged is effectively automatically wrapped in Control.BeginInvoke, which "posts a message to invoke a callback" to the dispatch queue of the window/thread.
Happy coding.
*Because the thread that created (or perhaps it's initiated?) the BGW effects where the callbacks will be posted, it is possible to create a BGW that will not "run" on the desired UI thread. To avoid this odd behavior, always create/start the BGW on the UI thread that it should post back to.
The answer is probably that cross thread operations can be executed. They're just prone to trouble. And a Control (which is supposed to throw an exception) probably doesn't 'mind' cross threading when it's aSleep.
Threads all share resources. That's the whole problem around multi-threaded operations.
MSDN says:
You must be careful not to manipulate any user-interface objects in your DoWork event >handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.
BackgroundWorker events are not marshaled across AppDomain boundaries. Do not use a BackgroundWorker component to perform multithreaded operations in more than one AppDomain.
And yet when I use the backgroundworker, it's not that I need to be careful not to manipulate any UI objects, it's that can't_ if I try to access the UI components from the DOWork event. The code compiles, but when the code for the DoWork runs, I get an error:
Cross-thread operation not valid: Control 'utAlerts' accessed from a thread other than the thread it was created on.
MSDN doesn't say anything about how is this done or why. Is the backgroundworker decorated with some attribute that prevents this? How is this accomplished?
If you're handler is an instance method in your UI class, you should have access to the members of that class.
it's that my app won't even compile if I try to access the UI components from the DOWork event.
This will only happen if your DoWork handler is static or in a different class than the UI components. In that case, you may not have access to them, as they are not visible to you.
Edit:
BackgroundWorker is intended to do "work" that is unrelated to your User Interface. You cannot change User Interface elements on any thread other than the UI thread, as user interface elements tend to have thread affinity. This actually has nothing to do with BackgroundWorker, but rather threading and user interface elements.
BW is intended to work around this by giving you progress and completion events that are automatically marshalled back onto the UI thread for you, allowing you to change UI elements there. However, you can always do this directly yourself via Control.Invoke in Windows Forms or Dispatcher.Invoke in WPF.
As to how this works - It depends on what framework you're using. For example, in Windows Forms, every Control (which is the base class of all of the UI elements) has a Handle, and the Handle is internally a native window handle. This handle is used to check the window's Thread ID against the current thread ID. This allows the check to be made without storing extra variables.
The error that you get when you try to change/update UI controls with a BackgroundWorker has nothing to do with sharing resources over the thread. It simply states that you cannot alter a control that was created on another thread.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
textBox1.Text = "Test";
}
Results in:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.
This is used so that multiple threads are not accessing/changing the same controls at the same time. BackgroundWorkers are Asynchronous and could cause a lot of problems if controls were updated while the main thread updating them as well.
I do not know how they achieved this, however, it is probably in the best interest that they prevented this from happening.
The MSDN provided another line of documentation to the segment you copied which states "BackgroundWorker events are not marshaled across AppDomain boundaries. Do not use a BackgroundWorker component to perform multithreaded operations in more than one AppDomain."
EDIT CORRESPONDES TO CONVERSATION IN COMMENTS:
private void Form1_Load(object sender, EventArgs e)
{
TextBox.CheckForIllegalCrossThreadCalls = false;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
textBox1.Text = "Test";
}
With the addition of the CheckForIllegalCrossThreadCalls = false, this code executes without error.
In the summary of the boolean property, it states that it indicates whether to "catch calls on the wrong thread."
It can be done with something as simple as having each control store the current thread (or maybe just its ID) in a private field in the constructor and then checking if the current thread is still that one before every method. Something like this:
class ThreadAffineObject
{
private readonly Thread originalThread;
public ThreadAffineObject()
{
this.originalThread = Thread.CurrentThread;
}
private void PreventCrossThreadOperation()
{
if(Thread.CurrentThread != originalThread)
throw new CrossThreadOperationException();
}
public void DoStuff()
{
PreventCrossThreadOperation();
// Actually do stuff
}
private int someField;
public int SomeProperty
{
get { return someField; } // here reading is allowed from other threads
set
{
PreventCrossThreadOperation(); // but writing isn't
someField = value;
}
}
}
I have some code that I wrote, which does what I want. However, I am not quite sure how, exactly, it works. The part I am having the most trouble with is the last part. I had a textBox1.Text = "test" which did not work. I got a run time error about it being called from a different thread. When I put the textBox1.Invoke(etc etc), it worked as expected. Why?
As you can see, I know just enough to be dangerous and I really want to understand what's going on here instead of blindly copying and pasting from sites around the web.
I have the following in a class named SerialCommunicator:
public SerialCommunicator(SerialPort sp)
{
this.sp = sp;
sp.ReceivedBytesThreshold = packetSize;
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
sp.Open();
}
public void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(50);
SerialPort s = (SerialPort)sender;
byte[] buffer = new byte[128];
s.Read(buffer, 0, s.BytesToRead);
}
Then, in my Form1.cs I have a button that when pressed does the following:
private void btnPortOK_Click(object sender, EventArgs e)
{
string comPort = cboComPorts.SelectedItem.ToString();
SerialPort sp = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One);
sp.DataReceived += new SerialDataReceivedEventHandler(DataHasBeenReceived);
comm = new SerialCommunicator(sp);
}
public void DataHasBeenReceived(object sender, EventArgs args)
{
textBox1.Invoke(new EventHandler(delegate { textBox1.Text += "test"; }));
}
This is thread-affinity. UI controls don't like to be touched by anything except the thread that created them, but the DataReceived thread happens from a different thread. Adding a call toControl.Invoke pushes an item of work back to the UI thread, so the Text updated can succeed.
I am not an expert on this (there will likely be better answers than this). But as I understand it, the GUI thread "owns" your form. So when you try to update it from a different thread you are crossing the streams.
The Invoke is a way to ask the GUI thread to run a method. Method that it runs is your textBox1.Text += "test";
The idea is by invoking a delegate, that will ask the GUI thread to make the change, rather than just changing the value yourself. This allows allow the change to be done in a thread safe manner.
Here is a good article by Jon Skeet on this issue:
http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
Events are called from the thread where they happen. (Unless specified otherwise).
Think about this way:
When you activate the event, it is actually called as a finction EventName(). So calling an event means actually going to all the methods that were registered to that event and doing them.
But, this is done in the same thread in a serial way.
So if an event happened in a thread that is not your UI thread you'll get theat error.
The issue is that the GUI components only accepts modifications from the GUI thread. So when other threads want to modify the GUI, then they must queue their modification code using measures like control.Invoke(...) which will queue the delegate to be processed as soon as possible on the GUI event queue, and thus the correct thread.
What you run in to is that one of the built-in checks are fired than controls that the calling thread indeed is the correct thread. It is a security measure that makes debugging easier (if they were not present you would have to debug subtle threading issues instead...)
textBox1.Text = "test" doesn't work because you are calling it from another thread (i.e. the DataHasBeenReceived event) then the thread who owns the textbox. That's usually the thread in which your application runs and that creates your GUI interface (and thus your textbox). Invoke works because that methods switches to the GUI thread, sets your text and then switches back to the thread of your DataHasBeenReceived event.
In Net 1.0 and 1.1 you could use GUI controls from another thread then then the one that owned them but this resulted in a lot of problems when threads started accessing the controls at the same time. So, since net 2.0 Microsoft changed that.
If you want to know if must use invoke or not (i.e. if a method can be called from the both the GUI thread or another thread), you can use the property InvokeRequired combined with an if else. A invoke call is slightly more expensive then a direct manipulation of the control.
I want to run an operation on a background thread. When it has completed I want to check for any errors that occurred and re-throw them on my original thread.
I am using a backgroundworker. Throwing an exception in the RunWorkerCompleted event handler results in an unhandled exception - this makes sense if the eventhandler is running on the background thread. If I had a winform control I could call Invoke or BeginInvoke but I do not have a winform control in this object, although it is a winform project.
How can I re-throw an exception that occurred in the backgroundworker?
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// I want to throw an exception here, without causing an unhandled exception and without being able to call Invoke or BeginInvoke on a WinForm control.
}
else if (e.Cancelled)
{
// Do something useful
}
else
{
if (e.Result != null)
{
// Do something with the result
}
}
}
I would have assumed that the RunWorkerCompleted event handler would be running on the original calling thread. Perhaps the backgroundworker is not what I need in this case.
It's not possible to inject code into another running thread. Not even the operating system can do this.
Control.BeginInvoke works by putting the delegate reference in a queue and then using PostMessage to post a user-message into the UI thread's message queue. The Application.Run message loop looks for this message and when it finds it pops the delegate off the queue and executes it.
The point is that there is no other way to do what you need without your main thread being coded to look for a some kind of signal (or message) from the other thread.
Added
You stated that this is a WinForm application but you do not have a Control to use BeginInvoke with.
Edit: I suggested a lazy-load without thinking it through. The Control might end up getting created on the wrong thread.
Pre-create a Control prior to Application.Run that lives for the lifetime of the app. You can use this to BeginInvoke from.
Edit #3
So then I try this to make certain it works and of course it doesn't. You can't simply create a generic Control, it must have an HWND handle. Simple fix: create it like this:
invokerControl = new Control();
invokerControl.CreateControl();
That will allow you to BeginInvoke from it, even if there are no open Form objects to invoke from.
You can check from other side. I mean - place timer (that will run in same main thread as form) on your form, and once per second - check some Exception field on your form (with lock()), and also some object field to detect that operation is completed. And then from bgw_RunWorkerCompleted wrap code with try...catch, and on catch (again with lock()) set Exception field of form to caught exception. But why not use Invoke or BeginInvoke?
If you didn't create the BGW instance on the UI thread then its RunWorkerCompleted event is going to run on an arbitrary threadpool thread. Any exception you throw on that thread is uncatchable and will terminate your app with a last gasp through AppDomain.UnhandledException.
In this case, there just isn't much use for BGW anymore. It is only nice to ensure that its events run on the UI thread. You might as well use MethodInvoker.BeginInvoke(). You'll need to think this through a bit and decide exactly what you're going to do when a bit of code off on some worker thread fails to do its job. Dealing with such a mishap is generally not possible and letting the program crash and burn is the right thing to do.
If you do want some kind of way to notify the user and try to keep the program stumbling along then you really ought to create the BGW instance on the UI thread. And use, say, MessageBox.Show() in the RunWorkerCompleted event handler. Be sure to recover your program state when you do this, you almost certainly need a catch clause in DoWork() to clean up the shrapnel.
Don't throw an exception.
Raise an event in the background worker which your main application thread subscribes to and then handle the error there - by throwing an exception if necessary.
Just handle the RunWorkerCompleted event. This event is synchronized for you
BackgroundWorker bgw;
private void button1_Click(object sender, EventArgs e)
{
bgw = new BackgroundWorker();
bgw.DoWork +=new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.RunWorkerAsync(bgw);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
textBox1.Text = e.Error.Message;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
throw new NotImplementedException();
}
If by chance you're using 4.0 you can switch to using a Task instead which will do what you want. See this example
I believe I have a potential threading issue. I have a user control that contains the following code:
private void btnVerify_Click(object sender, EventArgs e)
{
if (!backgroundWorkerVerify.IsBusy)
{
backgroundWorkerVerify.RunWorkerAsync();
}
}
private void backgroundWorkerVerify_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
VerifyAppointments();
}
private void backgroundWorkerVerify_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Information was Verified.", "Verify",
MessageBoxButtons.OK, MessageBoxIcon.Information);
CloseEvent();
}
vanilla code. but the issue I have is that when the application is running and the users tabs to another application when they return to mine the application is hung, they get a blank screen and they have to kill it. This started when I put the threading code. Could I have some rogue threads out there? what is the best way to zero in a threading problem? The issue can't be recreated on my machine...I know I must be missing something on how to dispose of a backgroundworker properly. Any thoughts are appreciated, Thanks,
Jose
Your code snippet doesn't explain it, but deadlocking the UI thread is never that difficult when you use BGW and are interested in its IsBusy property. A deadlock like this is usually easy to diagnose, use Debug + Break All. Then Debug + Windows + Threads and double-click the Main Thread. Then Debug + Windows + Call Stack to see what the UI thread is doing.
The common scenario is that the UI thread is looping on the IsBusy property. The BGW can't complete because its RunWorkerCompleted event can't run until the UI thread goes idle.
Are you accessing the GUI from the VerifyAppointments() method? You should utilize the DoWorkEventArgs in order to pass in the arguments you are verifying and you should not access the GUI from the BackgroundWorker directly.
You can safely access the GUI only in the RunWorkerCompleted or the ProgressChanged events.