I have two threads that look like this
pbDB_running = true; // start progress bar
Thread connectDb = new Thread(new ThreadStart(ConnectToDb));
Thread runProgress = new Thread(new ThreadStart(RunpbDB));
connectDb.Start();
runProgress.Start();
connectDb.Join(); //wait untill the connection is done
pbDB_running = false; //stop the progress bar
as you probably might have guessed, ConnectToDb is used to make a connection to a database, while runpbDB is making a progress bar run on the interface. The progress bar (pbDB) is a Windows.Forms control created with drag and drop on the design view.
The runProgress thread is running RunpbDB() wich looks like this :
private void RunpbDB()
{
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}
When the two threads start I get the following exception inside RunpbDB() :
Cross-thread operation not valid: Control 'pbDB' accessed from a thread other than the thread it was created on.
What can I do to overcome this situation?
Have you thought about using a BackgroundWorker? This might make your life a lot easier. You could set two up, one for your database call and the other for your progress bar. Just listen for the background workers ProgressChanged and RunWorkerCompleted events.
More information on MSDN
Use Control.Invoke method to remedy this problem. The whole your solution will became
private void RunpbDB()
{
while (pbDB_running)
{
Invoke((Action)(()=>{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;}));
}
Invoke((Action)(()=>{pbDB.Value = 0;});
}
You can use something along the lines of pbDB.InvokeRequired and if so, call pbDB.Invoke to perform your action back on the UI thread.
You do not need the check if you know it will always be done on a separate thread than the UI thread.
Here is a link to some code on this and other ways to accomplish this.
You could also use a BackgroundWorker
This is a security imposed by microsoft for its .NET technology. It basically happens when you access a winforms element from a separate thread, i.e. not in the main thread where the GUI winforms is running. The solution is to create a delegate for your RunpbDB method. See the solution here Best Way to Invoke Any Cross-Threaded Code?. in here too: How to update the GUI from another thread in C#?
Just make your life easier and use a BackgroundWorker for this if you don't have access to .NET 4.0. If you can use 4.0+, use the TPL. And if you can use 4.5, you can use the new async/await functionality. There are tons of examples here on Stack Overflow. Here is a link from Stephen Cleary comparing them.
Cross thread operation call when UI thread is involved was discouraged by VS team in 2.0(before that it was possible for security reasons. There are two ways to overcome this issue. Easy way is to set the static property
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls
to false which then disables this check globally from your application. But this solution is not advised by any one and not even by me since it again opens the security holes.
Another ways is, in the method first check if UI control needs Invoke,If so then use control's invoke method to invoke the current method again and then return. Code can better clear what I want to say so
private void RunpbDB()
{
if (pbDB.InvokeRequired)
{
pbDB.Invoke(new Action(RunpbDB));
return;
}
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}
Related
I have a class called Form1, which has a button in it. Now in that class I made another thread.
If I try to change the button in any way from the new thread, I get the cross-thread error/exception
new Thread(delegate ()
{
while (!DL.HasExited)
{
Thread.Sleep(500);
}
File.Delete(folderBrowserDialog1.SelectedPath + #"\Steam\steamcmd.zip");
//The code below this note is the problem
button1.Text = "START DOWNLOADING";
button1.Enabled = true;
}).Start();
I need to have the code in the new Thread, because I don't want to make my program freeze when it reaches the while loop.
So how can I change the button text from a different thread?
you cannot access ui element properties from a different thread. Use beginInvoke
button1.BeginInvoke( new MethodInvoker(() =>
{
button1.Text = "START DOWNLOADING";
button1.Enabled = true;
}));
Use Invoke(). This function takes a delegate (you can pass an anonymous function too). Something like:
Invoke(() => {
button1.Text = "START DOWNLOADING";
button1.Enabled = true;});
Threads are not meant to mess with each other's memory space - thus, your attempt to simply change the text will fail. However, there are 3 solutions I could think of:
Using invoke()
As others mentioned a few seconds before I did, you could use invoke to change the text in another form. However, if you'd like any more communication between the threads, this would be inefficient.
Using shared resources
In order to converse between threads, you can use a common resource that will be used for their communication. A very basic example of this is writing from the thread to a text file the text you want the button to display, and reading it each few seconds from the other thread and checking for changes. There are some better ways to do this, this is just an example. If you'd like me to show you an example of this method, ask for it and I'll gladly provide it.
Using processes instead of threads
Proccesses and threads both allow the multytasking you need, however, processes can interfere with each other. There are some more diffrences you should read about before making this decision, which leaves it up for you - which do you think is more fit for each one of these, a process or a thread? I'll happily provide an example of the usage of proccesses if you'd like me too, as well.
Good luck :)
The simplest way to achieve what you want is to use the BeginInvoke function on the control:
public delegate void InvokeDelegate();
new Thread(delegate ()
{
while (!DL.HasExited)
{
Thread.Sleep(500);
}
File.Delete(folderBrowserDialog1.SelectedPath + #"\Steam\steamcmd.zip");
//The code below this note is the problem
button1.BeginInvoke(new InvokeDelegate(InvokeMethod));
}).Start();
public void InvokeMethod()
{
button1.Text = "START DOWNLOADING";
button1.Enabled = true;
}
I am very new to WPF. And just started learning threading.
Here is my scenario:
I've created a program with a button named START. When start button is clicked it starts to do some complex task in different thread. Just before beginning the complex task it also creates a UI elements in another STA thread (technically i don't know what i am saying).
Here is a sample code:
// button click event
private void button1_Click(object sender, RoutedEventArgs e)
{
System.Threading.Thread myThread = new System.Threading.Thread(
() => buttonUpdate("Hello "));
myThread.Start();
}
private void buttonUpdate(string text)
{
System.Threading.Thread myThread = new System.Threading.Thread(createUI);
myThread.SetApartmentState(System.Threading.ApartmentState.STA);
// set the current thread to background so that it's existant will totally
// depend upon existance of main thread.
myThread.IsBackground = true;
myThread.Start();
// Please don't read this loop it's just for my entertainment!
for (int i = 0; i < 1000; i++)
{
System.Threading.Thread.Sleep(100);
button1.updateControl(new Action(
() => button1.Content = text + i.ToString()));
if (i == 100)
break;
}
// close main window after the value of "i" reaches 100;
this.updateControl(new Action(()=>this.Close()));
}
// method to create UI in STA thread. This thread will be set to run
// as background thread.
private void createUI()
{
// Create Grids and other UI component here
}
The above code succesfully does what i want to do. But do you think it's the correct way? so far i don't have any problem here.
EDIT: OOps I forgot to mention this class:
public static class ControlException
{
public static void updateControl(this Control control, Action code)
{
if (!control.Dispatcher.CheckAccess())
control.Dispatcher.BeginInvoke(code);
else
code.Invoke();
}
}
If you are using .NET 4.0 you might want to consider using the Task class from the Task parallel library. Read into it since you say you are new to threading. It's much more flexible to use.
Also I think that this link could be very helpful to you:
http://www.albahari.com/threading/
There seems to be no good reason to use 2 threads.
You should be able to execute the createUI() on the main thread. That'll be complicated enough when it becomes time to fill those controls.
Only one thread can interact with the UI. If you are going to add a control to a page or windows then you must use the thread that created the page or window. The typical scenario is to use threading to create expensive data or object in the background and then on the callback (running on the primary thread) retrieve the result and bind appropriate data to the UI. Look at using BackgroundWorker as it takes care of a lot of the threading detail for you. http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx Why do you want to create UI objects on another thead?
There is a sequence for FORM(some UI) should get downloaded using service.
Currently, this download is in a BackgroundWorker Thread.
Now, since the performance is slow... We decided to categories the FORMS into 2 and start downloading parallely using another BackgroundWorker on top of the existing Thread.
Now, the scenario is the either of this BackgroundWorker should wait for other to complete.
So, how to implement it.
I tried with AutoResetEvent. but, i could not achieve this.
Any help is appreciated.
I don't think that the scenario is really that one BackgroundWorker should wait for another. What you really want is to fire some UI event after (and only after) both of them complete. It's a subtle but important difference; the second version is a lot easier to code.
public class Form1 : Form
{
private object download1Result;
private object download2Result;
private void BeginDownload()
{
// Next two lines are only necessary if this is called multiple times
download1Result = null;
download2Result = null;
bwDownload1.RunWorkerAsync();
bwDownload2.RunWorkerAsync();
}
private void bwDownload1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
download1Result = e.Result;
if (download2Result != null)
DisplayResults();
}
private void bwDownload2_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
download2Result = e.Result;
if (download1Result != null)
DisplayResults();
}
private void DisplayResults()
{
// Do something with download1Result and download2Result
}
}
Note that those object references should be strongly-typed, I just used object because I don't know what you're downloading.
This is really all you need; the RunWorkerCompleted event runs in the foreground thread so you actually don't need to worry about synchronization or race conditions in there. No need for lock statements, AutoResetEvent, etc. Just use two member variables to hold the results, or two boolean flags if the result of either can actually be null.
You should be able to use two AutoResetEvent's and the WaitAll function to wait for both to complete. Call the Set function on the AutoResetEvent objects in the respective OnRunWorkerCompleted event.
Jeffrey Richter is THE guru when it comes to multi threading and he's written an amazing library called Power Threading Library which makes doing tasks like downloading n files asynchronously and continuing after they are all completed (or one or some), really simple.
Take a little time out to watch the video, learn about it and you won't regret it. Using the power threading library (which is free and has a Silverlight and Compact Framework version also) also makes your code easier to read, which is a big advantage when doing any async stuff.
Good luck,
Mark
int completedCount = 0;
void threadProc1() { //your thread1 proc
//do something
....
completedCount++;
while (completedCount < 2) Thread.Sleep(10);
//now both threads are done
}
void threadProc2() { //your thread1 proc
//do something
....
completedCount++;
while (completedCount < 2) Thread.Sleep(10);
//now both threads are done
}
Just use 2 BackgroundWorker objects, and have each one alert the UI when it completes. That way you can display a spinner, progress bar, whatever on the UI and update it as download results come back from the threads. You will also avoid any risks of thread deadlocking, etc.
By the way, just so we are all clear, you should NEVER call a blocking function such as WaitAll from the UI thread. It will cause the UI to completely lock up which will make you users wonder WTF is going on :)
I am just working on my first GUI application on Windows.
I have a WPF GUI to a small C# utility which copies files. When the button is clicked to copy, I obviously don't want the GUI to hang. So, I fire off a new thread to run the method which copies the files. I assume I'm on track so far and there's no "better" way of doing it in C#?
Now, I have a ProgressBar which I want to appear filled when the thread is done. (It's fine running as indeterminate for now). How do I check when the copying is done?
So, so far I have:
Thread t = new Thread(delegate()
{
po.Organise(inputPath, outputPath, recursive);
});
t.Start();
PBar.IsIndeterminate = true;
And I want something after that that works like:
if (t.Done)
{
PBar.Value = 100;
}
Have a look at the BackgroundWorker class. It supports events like RunWorkerCompleted or ProgressChanged.
Have a look here, too (this is about threading in general + backgroundworker, again).
As already stated, consider the use of the BackgroundWorker class. It was designed for these situations and exposes events suited for what you are trying to accomplish.
Use the ProgressChangedevent to report progress incrementally and the RunWorkerCompleted for when the task finishes. Check the MSDN page for code samples.
Wrap the if (t.Done) block in its own method. Invoke this method from the end of your worker thread.
Also, you might want to give the worker thread a name to make it easier to spot in the debugger.
You need a callback method. This should get you started. It uses an AsyncCallback, which is the best way to tackle this type of issue.
I just looked up an example I've been using for a project and stripped out the code specific to my app:
System.Windows.Forms.MethodInvoker mi = new System.Windows.Forms.MethodInvoker(delegate()
{
// Do your file copy here
});
AsyncCallback ascb = new AsyncCallback(delegate(IAsyncResult ar)
{
this.Dispatcher.Invoke(new ThreadStart(delegate (){
// set progressbar value to 100 here
}), null);
});
mi.BeginInvoke(ascb, null);
The quick and easy hack would be to just update the UI at the end of your anonymous method in your thread. Obviously you can't update it directly, but you can use Dispatcher.Invoke:
Thread t = new Thread(delegate()
{
po.Organise(inputPath, outputPath, recursive);
Dispatcher.Invoke(new Action(()=>{PBar.Value = 100;}),null);
});
t.Start();
As a general Windows programming principal, you have to make calls to update the UI from the UI thread (the one that is processing messages through a message pump).
In Windows Forms, the way that this was done was through the implementation of the ISynchronizeInvoke interface on the Control class, primarily through the implementation of the Invoke method.
With the release of .NET 2.0, it was realized that a better mechanism was needed to marshal calls into the correct context. That's where the SynchronizationContext comes in.
This class abstracts the interface you would use for marshaling calls to different contexts, allowing for specific implementations depending on the context.
So whether or not Windows Forms is the environment, or WPF, one call can be made in the same way across those contexts with the same effect (marshaling the call).
In your particular case, because you are using a closure (anonymous method), you can take advantage of the fact that a SynchronizationContext is available to you (through the static Current property) at the invocation site of the Thread to provide the mechanism to call back to the UI thread from your background thread:
// Get the synchronization context.
// This is in the UI thread.
SynchronizationContext sc = SynchronizationContext.Current;
// Create the thread, but use the SynchronizationContext
// in the closure to marshal the call back.
Thread t = new Thread(delegate()
{
// Do your work.
po.Organise(inputPath, outputPath, recursive);
// Call back using the SynchronizationContext.
// Can call the Post method if you don't care
// about waiting for the result.
sc.Send(delegate()
{
// Fill the progress bar.
PBar.Value = 100;
});
});
// Make the progress bar indeterminate.
PBar.IsIndeterminate = true;
// Start the thread.
t.Start();
Note, if you don't care about waiting for the result of the call back to the UI thread, you can make a call to the Post method instead, which will dispatch the call to the UI thread without waiting for that call to complete.
I haven't done much multithreading before and now find the need to do some background work and keep the UI responsive. I have the following code.
data.ImportProgressChanged += new
DataAccess.ImportDelegate(data_ImportProgressChanged);
Thread importThread = new Thread(
new ThreadStart(data.ImportPeopleFromFAD));
importThread.IsBackground = true;
importThread.Start();
void data_ImportProgressChanged(int progress)
{
toolStripProgressBar.Value = progress;
}
//In my data object I have
public void ImportPeopleFromFAD()
{
ImportProgressChanged(someInt);
}
But the UI doesn't get updated since the ImportProgressChanged() call is made on the background thread. In objective C I know you can use performSelectorOnMainThread and pass it a method to call using the main thread. What is the equivalent way of calling ImportProgressChanged() from the main thread?
(Assuming Windows Forms.) You can use Control.Invoke or Control.BeginInvoke - but a cleaner way may be to use BackgroundWorker to start with.
In WPF you'd use a Dispatcher instead of Control.Invoke, btw. See this WPF threading model guide for more details.
EDIT: Personally I probably wouldn't bother testing InvokeRequired first - I'd just call Invoke or BeginInvoke. If you're already "on" the right thread it won't do any significant harm.
For progress bars, however, BackgroundWorker is definitely the way forward.
Instead of updating the GUI, data_ImportProgressChanged should throw an exception when it gets called changes the progressbar.
The shortest change is to use Control.InvokeRequired and .Invoke(), but the Backgroundworker was especially created for this scenario .
If you want to solve it in the data object you will have to make that depend on the GUI so its better to solve this in the handler:
void data_ImportProgressChanged(int progress)
{
if (toolStripProgressBar.InvokeRequired)
{
Action<int> a = new Action(data_ImportProgressChanged);
toolStripProgressBar.Invoke(a, progress);
}
else
toolStripProgressBar.Value = progress;
}
You already have your answer but I'll add my solution anyway:
void data_ImportProgressChanged(int progress)
{
if (InvokeRequired)
{
BeginInvoke(new Action<int>(data_ImportProgressChanged),progress);
return;
}
toolStripProgressBar.Value = progress;
}