I have a event handler attached to the selectionChanged event on a DataGridView. In this handler I need to create and load an image and then display it in a picture box. The trouble I'm having is, if I jump between row selections quickly the application seems to hang, which is the issue I was trying to avoid.
Here is my code:
private void loadJobSheet(Job currentJob)
{
if (this.jobCardImageThread != null && this.jobCardImageThread.IsAlive)
this.jobCardImageThread.Abort();
Image jobCardImage = null;
this.jobCardImageThread = new Thread(new ThreadStart(
delegate()
{
SavedDocument document = currentJob.SavedDocument;
DocumentConverter<Bitmap> converter = DocumentConverterFactory<Bitmap>.getDocumentConverterForType(Path.GetExtension(document.Document_Name).Replace('.', ' ').Trim().ToUpper(), typeof(Bitmap));
jobCardImage = (Image)converter.convertDocument(FileUtils.createTempFile(document.Document_DocumentData.ToArray(), document.Document_Name));
}
));
jobCardImageThread.Start();
this.picLoadingJobCard.Visible = true;
jobCardImageThread.Join();
if (jobCardImage != null)
{
this.picJobCard.Image = jobCardImage;
this.picLoadingJobCard.Visible = false;
}
}
You are waiting for the separate thread to finish when you do
jobCardImageThread.Join();
This blocks the UI thread, which suspends the application.
You should remove the Join() call, create a separate method out of anything after the Join() call, and call that method from the delegate. Probably use an Invoke(...) call to switch back to the UI thread.
I think your problem is jobCardImageThread.Join(); With this statement you tell your Thread to wait for the other to finish. This way your UI is hanging.
Why don't you use a background-worker. For example:
Put this into your constructor
this.backgroundWorker = new BackgroundWorker();
this.backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
this.backgroundWorker.WorkerSupportsCancellation = true;
And add the following methods:
private BackgroundWorker backgroundWorker;
private AutoResetEvent resetEvent = new AutoResetEvent(false);
private Thread thread;
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
this.picLoadingJobCard.Visible = true;
Job currentJob = (Job)e.Argument;
SavedDocument document = currentJob.SavedDocument;
DocumentConverter<Bitmap> converter = DocumentConverterFactory<Bitmap>.getDocumentConverterForType(Path.GetExtension(document.Document_Name).Replace('.', ' ').Trim().ToUpper(), typeof(Bitmap));
Image jobCardImage = (Image)converter.convertDocument(FileUtils.createTempFile(document.Document_DocumentData.ToArray(), document.Document_Name));
e.Result = jobCardImage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//error-handling
}
else if (e.Cancelled)
{
//cancel-handling
}
else
{
Image jobCardImage = e.Result as Image;
if (jobCardImage != null)
this.picJobCard.Image = jobCardImage;
}
this.picLoadingJobCard.Visible = false;
this.resetEvent.Set();
}
private void loadJobSheet(Job currentJob)
{
if (this.thread != null)
this.thread.Abort();
this.thread = new Thread(new ThreadStart(
delegate()
{
if (this.backgroundWorker.IsBusy)
{
this.backgroundWorker.CancelAsync();
this.resetEvent.WaitOne();
}
this.backgroundWorker.RunWorkerAsync(currentJob);
}));
this.thread.Start();
}
If you create a background Thread and immediately call Join after running it, you basically just wasted time and memory for creating a synchronous method, because your current thread will block until the background thread is finished. If the current thread is a UI thread, this will be pretty obvious.
Also, using Thread.Abort to kill a thread is not recommended.
I would suggest creating a long-lived background thread which will most of the time wait for a signal from the main thread. This will ensure that you don't unnecessarily create multiple threads in case you are receiving more requests than your worker method can handle.
This is the general idea:
// have a long lived and prosperous thread which handles jobs
private readonly Thread _backgroundWorker;
// you need a way to signal the thread to continue running
private readonly AutoResetEvent _signalNewTask;
// you need a flag indicating you want to stop (to avoid aborting the thread)
private volatile bool _keepRunning;
// and you need to pass the current job to that thread
private volatile Job _currentJob;
The loop should look something like this:
// this runs on a background thread
private void WorkerLoop()
{
Job lastJob = null; Image lastResult = null;
while (_keepRunning)
{
// use an AutoResetEvent for cross-thread signalization
_signalNewTask.WaitOne();
// make sure the app isn't ending
if (!_keepRunning)
break;
// don't bother if we already processed this job
if (lastJob == _currentJob)
continue;
// capture the job in a local variable
lastJob = _currentJob;
// long processing
lastResult = LoadImage(lastJob);
// check if this is still the last requested job
if (_keepRunning && lastJob == _currentJob)
DisplayImage(lastResult);
}
}
To schedule a job for execution, you simply set the field and signal the event:
private void ScheduleNewJob(Job nextJob)
{
// don't waste time if not needed
if (nextJob == _currentJob)
return;
_picLoadingJobCard.Visible = true;
_currentJob = nextJob;
_signalNewTask.Set();
}
You'll also need to add initialization and cleanup code to your Form:
public SomeForm()
{
InitializeComponent();
_keepRunning = true;
_signalNewTask = new AutoResetEvent(false);
_backgroundWorker = new Thread(WorkerLoop);
_backgroundWorker.IsBackground = true;
_backgroundWorker.Priority = ThreadPriority.BelowNormal;
_backgroundWorker.Start();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
// set the running flag to false and signal the thread
// to wake it up
_keepRunning = false;
_signalNewTask.Set();
// this will lock very shortly because the background
// thread breaks when the flag is set
_backgroundWorker.Join();
base.OnFormClosed(e);
}
And since DisplayImage (or whatever) will be called from a background thread, you have to schedule on the UI thread by calling Invoke:
private void DisplayImage(Image result)
{
if (this.InvokeRequired)
{
Invoke(new Action<Image>(DisplayImage), result);
return;
}
_picLoadingJobCard.Visible = false;
_picJobCard.Image = result;
}
Related
Hi guys I start threads with such code:
Thread[] thr;
private void button1_Click(object sender, EventArgs e)
{
decimal value = numericUpDown2.Value;
int i = 0;
threads_count = (int)(value);
thr = new Thread[threads_count];
for (; i < threads_count; i++)
{
thr[i] = new Thread(new ThreadStart(go));
thr[i].IsBackground = true;
thr[i].Start();
}
}
How to stop all them if my condition become true
A number of the answers say to abort the thread. Never abort a thread unless it is an emergency situation and you are shutting down the application.
The CLR guarantees that its internal data structures are not corrupted by a thread abort. This is the only (*) guarantee made by the CLR with respect to thread aborts. It specifically does not guarantee:
That the thread actually will abort. Threads can harden themselves against being terminated.
That any data structure that is not in the CLR itself will be uncorrupted. Thread aborts in the middle of crucial operations can leave BCL data structures or user data structures in arbitrarily inconsistent states. This can crash your process mysteriously later.
That locks will be released. Aborting threads can cause locks to be held forever, it can cause deadlocks, and so on.
In case I am not being clear: it is insanely dangerous to abort a thread and you should only do so when all the alternatives are worse.
So what if you want to start up a thread and then shut it down cleanly?
First, don't do that. Don't start a thread in the first place. Start a Task<T> with a cancellation token and when you want to shut it down, signal its cancellation token.
If you do have to start a thread, then start the thread such that there is some mechanism whereby the main thread and the working thread can cleanly and safely communicate "I want you to shut yourself down cleanly at this time".
If you don't know how to do that then stop writing multithreaded code until you learn how to do that.
(*) This is a small lie; the CLR also makes certain guarantees with respect to the interactions of thread aborts and special code regions such as constrained execution regions and finally blocks.
You can use a CancellationToken to signal when the operation should stop.
Create a CancellationTokenSource as an instance field of your type that you initialize in the button click handler.
In your background method periodically check the IsCancellationRequested property of the Token in the token source, or call ThrowIfCancellationRequested() if you want it to just throw an exception if it is canceled.
When you want to stop the threads call Cancel on the token source.
Brutal way (not recommended) - use Thread.Abort method to abort threads. This method raises ThreadAbortException on thread. Like this:
foreach(Thread thread in thr)
thread.Abort();
But better way is notifying thread about cancellation and letting it correctly finish its job. You can do it simply with .Net 4 tasks:
Task[] thr = new Task[threads_count];
var source = new CancellationTokenSource();
for (int i = 0; i < threads_count; i++)
{
thr[i] = Task.Factory.StartNew(go, source.Token);
}
// later, when condition is met
source.Cancel();
And here is how cancellation should look like:
private static void go(object obj)
{
CancellationToken token = (CancellationToken)obj;
while (true)
{
if (token.IsCancellationRequested)
return;
// do some work
}
}
If you want to know how to terminate the thread gracefully, I'd recommend you to take a look the following example on MSDN:
using System;
using System.Threading;
public class Worker
{
public void DoWork()
{
while (!_shouldStop)
{
Console.WriteLine("worker thread: working...");
}
Console.WriteLine("worker thread: terminating gracefully.");
}
public void RequestStop()
{
_shouldStop = true;
}
// Volatile is used as hint to the compiler that this data
// member will be accessed by multiple threads.
private volatile bool _shouldStop;
}
public class WorkerThreadExample
{
static void Main()
{
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);
workerThread.Start();
Console.WriteLine("main thread: Starting worker thread...");
while (!workerThread.IsAlive); // Loop until worker thread activates
// Put the main thread to sleep for 1 millisecond to
// allow the worker thread to do some work:
Thread.Sleep(1);
workerObject.RequestStop();
// Use the Join method to block the current thread
// until the object's thread terminates.
workerThread.Join();
Console.WriteLine("main thread: Worker thread has terminated.");
}
}
This is Windows Form Code in which:
1) On Clicking start button, Main Thread creates another Thread
2) Again created Thread creates on more Thread.
3) On clicking Stop button, First the last Thread should terminate Then the Thread created by Main thread should Terminate.
namespace Thread_TerminateProblem
{
public partial class Form1 : Form
{
private static AutoResetEvent m_ResetEvent = null;
private static ManualResetEvent m_ResetEvent_Thread = new ManualResetEvent(false);
enum ServiceState { Start, Stop };
bool flag = false;
int x = 0;
ServiceState _state;
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
flag = true;
_state = ServiceState.Start;
m_ResetEvent = new AutoResetEvent(true);
Thread t1 = new Thread(fun_Thread1);
t1.Start();
t1.Name = "Thread1";
}
private void btnStop_Click(object sender, EventArgs e)
{
_state = ServiceState.Stop;
m_ResetEvent.Set();
}
private void fun_Thread1()
{
while (true)
{
m_ResetEvent.WaitOne();
switch (_state)
{
case ServiceState.Start:
{
Thread t = new Thread(fun_Thread2);
t.Start();
t.Name = "Thread2";
break;
}
case ServiceState.Stop:
{
m_ResetEvent_Thread.Set();
flag = true;
break;
}
}
// When the child Thread terminates, Then only this thread should terminate
if (flag == true)
{
// Waiting for notification from child Thread
notifyParent.WaitOne();
Thread.Sleep(100);
break;
}
m_ResetEvent.Reset();
}
}
private static ManualResetEvent notifyParent = new ManualResetEvent(false);
private void fun_Thread2()
{
while (true)
{
if (m_ResetEvent_Thread.WaitOne(1, false))
{
notifyParent.Set();
break;
}
x++;
}
}
}
}
simplistic answer is to use the thread Abort() method however your code does not really make it clear what condition,
what loop tests vs a condition? why do you need to abort a thread? I am asking as there may be a better way to approach this
I am working with Background Worker but neither i am able to synchronize my progress bar nor able to stop or abort the process.
in my dowork function
void bw_DoWork(object sender, DoWorkEventArgs e)
{
if(bw.CancellationPending==true)
{
e.cancel=true;
return;
}
else
{
e.Result = abc();
}
}
int abc()
{
//my work
Count++;
return count;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(bw.CancellationPending==true)
{
button17.Visibility = Visibility.Visible;
label1.Content = "Aborted";
}
button17.Visibility = Visibility.Visible;
label1.Content = "Completed";
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
if(bw.IsBusy)
{
bw.CancelAsync();
}
}
Now i want to know how could i Synchronize my Progress Bar and how to exit from the process?
Have you set the BackgroundWorker.WorkerReportsProgress && BackgroundWorker.WorkerSupportsCancellation properties on your instance to be true?
e.g.
var myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.WorkerReportsProgress = true;
myBackgroundWorker.WorkerSupportsCancellation = true;
//the rest of the init
If you want to report progress, you need to call the BackgroundWorker.ReportProgress() method from inside your DoWork.
This is a rubbish and trivial answer but give the Task Parallel library a whirl.
http://msdn.microsoft.com/en-us/library/dd537608.aspx
This library encapsulates threads as discrete Task objects. It supports cancellation.
Be warned that in a worker thread, pause and cancellation operation have to be supported by the worker code itself, by polling pause/cancel flags and tokens. You cannot safely achieve these operations with threads alone.
It is a nicer pattern to work with
As for your question, 2 flags are required to support your operations. You will be need to check them at intervals during the worker code.
bool pause = false;
bool cancel = false;
void DoWork()
{
try
{
...
//periodically check the flags
if(cancel) return;
while(paused){}; //spin on pause
...
}
finally
{
//cleanup operation
}
}
Alastair Pitts' answer illustrates how background worker supports these features.
So does MSDN ;) http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
(You might want to check out this other SO question/answer for details about the new async facility! It greatly improves the quality of life of developing this kind of operations!)
BackgroundWorker is event-based, basic usage is the following (the link provides many useful additional details):
var worker = new BackgroundWorker();
// The following two props must be true:
// #1: The worker will be enabled to signal its progress
worker.WorkerReportsProgress = true;
// #2: The worker will accept cancellation
worker.WorkerSupportsCancellation = true;
// Now the events:
worker.DoWork += (s,e) =>
{
int i = 0; // This goes from 0 to 100
// Do code, update 'i'
worker.ReportProgress(i);
worker.CancelAsync(); //... to cancel the worker if needed
// WARNING: This code *cannot* interact with the UI because
// it's running in a different thread
};
worker.ProgressChanged += (s,e)=>
{
// This is executed when you call ReportProgress() from DoWork() handler
// IMPORTANT: All UI interaction **must** happen here
// e.ProgressPercentage gives you the value of the parameter you passed to
// ReportProgress() (this mechanism is a perfect fit for a progress bar!)
};
worker.RunWorkerCompleted+= (s,e) =>
{
// code here runs when DoWork() is done, is canceled or throws.
// To check what happened, the link provides this sample code:
if (e.Cancelled == true)
{
// Cancelled!
}
else if (e.Error != null)
{
// Exception !
}
else
{
// Work completed!
}
};
worker.RunWorkerAsync();
It's important to know that (extracted from the link above):
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.
UPDATE Lambdas here are used to keep code compact. You can obviously use "normal" handlers or whatever other method of attaching code to events you like/want/need.
I'm trying to enable a busy indicator on log in. The problem I'm having is it won't enable until everything is done executing. How can I immediately tell the thread to update the UI as soon as I log in to start the indicator asap?
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
radBusyIndicator.IsBusy = true;
//var backgroundWorker = new System.ComponentModel.BackgroundWorker();
//backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(backgroundWorker_DoWork);
//backgroundWorker.RunWorkerAsync();
}));
string error = string.Empty;
long userId = 0;
//Login code here....
//........... bunch of other code. etc..
}
The UI will update as soon as the UI thread is free. There is no need for Dispatcher.Invoke in this case, as you're already in the UI thread.
The key here is to move the "work" into a background thread, ie:
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
radBusyIndicator.IsBusy = true;
LoginButton.IsEnabled = false; // Prevent clicking twice
string error = string.Empty;
long userId = 0;
// Start this in the background
var task = Task.Factory.StartNew(()=>
{
//Login code here....
//........... bunch of other code. etc..
});
// Run, on the UI thread, cleanup code afterwards
task.ContinueWith(t =>
{
// TODO: Handle exceptions by checking t.Exception or similar...
radBusyIndicator.IsBusy = false;
LoginButton.IsEnabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
If you're using C# 5, you can simplify this by making your login and other code asynchronous:
private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
radBusyIndicator.IsBusy = true;
LoginButton.IsEnabled = false; // Prevent clicking twice
long userId = 0;
// Call async method with await, etc...
string error = await DoLoginAsync(userId);
var result = await BunchOfOtherCodeAsync();
radBusyIndicator.IsBusy = false;
LoginButton.IsEnabled = true;
}
You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on..
for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
this way it will not cause any thread Error too..
Just Enable your BusyIndicator as Your TimeTaking TAsk start and when the timeTaking Task is done just Disable your Busy Indicator in RUnWorkerCompleted Event.
I'm using the next code to do what I'm asking for :
private delegate void CallerDelegate(object e);
CallerDelegate caler = new CallerDelegate(MethodToCall);
on button click event :
if (currBusyThrd != null && currBusyThrd.IsAlive)
{
currBusyThrd.Abort();
}
ThreadPool.SetMaxThreads(1, 1);
//queue the work for thread processing
ThreadPool.QueueUserWorkItem(new WaitCallback(WaitCallbackMethod))
"WaitCallbackMethod" Method is :
void WaitCallbackMethod(object stateInfo)
{
// argList : i put some argument in a list to use it in "MethodToCall" ...
BeginInvoke(caler,argList);
}
and the method i'm calling by the thread is :
void MethodToCall(object args)
{
//Here I get the thread I'm calling to stop it when btn clicked again
currBusyThrd = Thread.CurrentThread;
// The rest of the code ...
}
I feel that this is wrong ...
How to do it right ?
Actually the calling will be by TextBox_KeyUp .. so every time the user enter a char the code will execute again .. and the BackgroundWorker didn't work .
One problem to this approach is that it's very dangerous to arbitrarily Abort a thread (in pretty much any language). There are too many issues that can popup around unfreed resources and misheld locks. It's typically best to set some kind of flag to ask the Thread to safely abort itself or to forget about the thread and let it run to completion.
Additionally, Aborting a Thread in the ThreadPool is very dangerous and I believe not a supported operation. The Threads in the ThreadPool are not owned by you and Aborting them cold have serious implications for the ThreadPool.
Here is the solution I would take.
private object m_lock = new object();
private bool m_isRunning = false;
private bool m_isAbortRequested = false;
public void OnButtonClick(object sender, EventArgs e) {
lock ( m_lock ) {
if ( m_isRunning ) {
m_isAbortRequested = true;
} else {
m_isAbortRequested = false;
m_isRunning = true;
ThreadPool.QueueUserWorkItem(BackgroundMethod);
}
}
}
private void BackgroundMethod() {
try {
DoRealWork();
} finally {
lock (m_lock) {
m_isRunning = false;
}
}
}
private void DoRealWork() {
...
if ( m_isAbortRequested ) {
return;
}
}
Yes, this is very wrong. You should never try to manually control a ThreadPool thread. If you need this sort of control, you should be using your own Thread object. In addition, Abort() is not the recommended way of ending a thread; you should have a control volatile bool on your form that the code in MethodToCall checks at various points and exits gracefully when it's true. While you can use the same approach with the ThreadPool, the fact that you need to be able to cancel seems to indicate that the process is long-running, or at least has the potential to be. The ThreadPool shouldn't be used for long-running processes.
For example...
private volatile bool stopThread = false;
private Thread workThread;
private void StartThread()
{
if(workThread == null)
{
stopThread = false;
workThread = new Thread(new ThreadStart(MethodToCall));
workThread.Start();
}
}
private void StopThread()
{
if(workThread != null)
{
stopThread = true;
workThread.Join(); // This makes the code here pause until the Thread exits.
workThread = null;
}
}
Then in MethodToCall, just check the stopThread boolean at frequent intervals and do any cleanup work that you need to do and exit the method. For example...
private void MethodToCall()
{
// do some work here and get to a logical stopping point
if(stopThread)
{
// clean up your work
return;
}
// do some more work and get to another stopping point
if(stopThread)
{
// clean up your work
return;
}
}
And just repeat that pattern.
For situations where one thread needs to 'signal' another thread to do something, I usually use a System.Threading.ManualResetEvent to signal the secondary thread to stop, like this:
private volatile bool _threadRunning = false;
private ManualResetEvent _signal = new ManualResetEvent(false);
private Thread _thread;
private void OnButtonClick(object sender, EventArgs e)
{
if (!_threadRunning) {
// Reset the 'signal' event.
_signal.Reset();
// Build your thread parameter here.
object param = ;
// Create the thread.
_thread = new Thread(ExecuteThreadLogicConditionally(param));
// Make sure the thread shuts down automatically when UI closes
_thread.IsBackground = true;
// Start the thread.
_thread.Start();
// Prevent another thread from being started.
_threadRunning = true;
} else {
// Signal the thread to stop.
_signal.Set();
// DO NOT JOIN THE THREAD HERE! If the thread takes a while
// to exit, then your UI will be frozen until it does. Just
// set the signal and move on.
}
}
// If the thread is intended to execute its logic over and over until
// stopped, use this callback.
private void ExecuteThreadLogicUntilStopped(object param)
{
// Use a while loop to prevent the thread from exiting too early.
while (!_signal.WaitOne(0)) {
// Put your thread logic here...
}
// Set the flag so anther thread can be started.
_threadRunning = false;
}
// If the thread logic is to be executed once and then wait to be
// shutdown, use this callback.
private void ExecuteThreadLogicOnce(object param)
{
// Put your thread logic here...
//
// Now wait for signal to stop.
_signal.WaitOne();
// Set the flag so another thread can be started.
_threadRunning = false;
}
// If the thread needs to be stopped at any point along the way, use
// this callback. The key here is to 'sprinkle' checks of the 'signal'
// to see if the thread should stop prematurely.
private void ExecuteThreadLogicConditionally(object param)
{
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Execute small chunk of logic here...
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Execute another small chuck of logic here...
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Continue this pattern through the method.
}
Note that this solution does not use the ThreadPool at all. It could easily be made to do so. And as a suggestion, I wouldn't muck with SetMaxThreads() function on the ThreadPool. Just let the ThreadPool do its thing. It's been designed to be optimal for the way you use it.
Try this code..
using System;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Thread workerThread = null;
ManualResetEvent threadInterrupt = new ManualResetEvent(false);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (this.workerThread == null)
{
this.threadInterrupt.Reset();
this.workerThread = new Thread(() =>
{
int i = 0;
while (!this.threadInterrupt.WaitOne(0))
{
Debug.Print("put your code in here while worker thread running.. " + i.ToString());
Thread.Sleep(100);
i++;
}
this.workerThread = null;
// worker thread finished in here..
});
this.workerThread.IsBackground = true;
// start worker thread in here
this.workerThread.Start();
}
else
{
// stop worker thread in here
threadInterrupt.Set();
}
}
}
}
I use BackgroundWorker most of the time in the win form apps to show progress as I'm getting data. I was under impression that Work_completed is guaranteed to be executed on Main UI thread but it's not. If we create a thread and call the worker.RunWorkerAsync within it, it breaks if we try to update any gui control. Here is an example
private void StartButton_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ThreadStart(PerformWorkerTask));
_worker = new BackgroundWorker();
thread1.Start();
}
public void PerformWorkerTask()
{
_worker.DoWork += delegate
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(100);
}
};
_worker.RunWorkerCompleted += delegate
{
// this throws exception
MessageLabel.Text = "Completed";
};
_worker.RunWorkerAsync();
}
How can we make backgroundworker work in this case?
RunWorkerAsync does its thread-synchronization magic by getting the SynchronizationContext from the thread that it is called on. It then guarantees that the events will be executed on the correct thread according to the semantics of the SynchronizationContext it got. In the case of the WindowsFormsSynchronizationContext, which is what is automatically used if you're using WinForms, the events are synchronized by posting to the message queue of the thread that started the operation. Of course, this is all transparent to you until it breaks.
EDIT: You MUST call RunWorkerAsync from the UI thread for this to work. If you can't do it any other way, your best bet is to invoke the beginning of the operation on a control so that the worker is started on the UI thread:
private void RunWorker()
{
_worker = new BackgroundWorker();
_worker.DoWork += delegate
{
// do work
};
_worker.RunWorkerCompleted += delegate
{
MessageLabel.Text = "Completed";
};
_worker.RunWorkerAsync();
}
// ... some code that's executing on a non-UI thread ...
{
MessageLabel.Invoke(new Action(RunWorker));
}
From your example it's hard to see what good the Thread (thread1) is, but if you really do need this thread1 then I think your only option is to use MainForm.Invoke() to execute RunWorkerAsync() (or a small method around it) on the main thread.
Added: You can use something like this:
Action a = new Action(_worker.RunWorkerAsync);
this.Invoke(a);
It sounds like the issue is just that you want to make a change to a GUI component and you aren't actually sure if you're on the GUI thread. Dan posted a valid method of setting a GUI component property safely, but I find the following shortcut method the simplest:
MessageLabel.Invoke(
(MethodInvoker)delegate
{
MessageLabel.Text = "Hello World";
});
If there are any issues with this approach, I'd like to know about them!
In the code you have presented here, you're adding the delegates for the BackgroundWorker events in a separate thread from the UI thread.
Try adding the event handlers in the main UI thread, and you should be okay.
You could probably make your existing code work by doing:
this.Dispatcher.BeginInvoke(() => MessageLabel.Text = "Completed")
instead of
MessageLabel.Text = "Completed"
You're probably having cross-thread data access issues, so you have to ensure that you access properties of MessageLabel on your UI thread. This is one way to do that. Some of the other suggestions are valid too. The question to ask yourself is: why are you creating a thread that does nothing other than create a BackgroundWorker thread? If there's a reason, then fine, but from what you've shown here there's no reason you couldn't create and start the BackgroundWorker thread from your event handler, in which case there would be no cross-thread access issue because the RunWorkerCompleted event handler will call its delegates on the UI thread.
I believe BackgroundWorker is designed to automatically utilize a new thread. Therefore creating a new thread just to call RunWorkerAsync is redundant. You are creating a thread just to create yet another thread. What's probably happening is this:
You create a new thread from thread 1 (the GUI thread); call this thread 2.
From thread 2, you launch RunWorkerAsync which itself creates yet another thread; call this thread 3.
The code for RunWorkerCompleted runs on thread 2, which is the thread that called RunWorkerAsync.
Since thread 2 is not the same as the GUI thread (thread 1), you get an illegal cross-thread call exception.
(The below suggestion uses VB instead of C# since that's what I'm more familiar with; I'm guessing you can figure out how to write the appropriate C# code to do the same thing.)
Get rid of the extraneous new thread; just declare _worker WithEvents, add handlers to _worker.DoWork and _worker.RunWorkerCompleted, and then call _worker.RunWorkerAsync instead of defining a custom PerformWorkerTask function.
EDIT: To update GUI controls in a thread-safe manner, use code like the following (more or less copied from this article from MSDN):
delegate void SetTextCallback(System.Windows.Forms.Control c, string t);
private void SafeSetText(System.Windows.Forms.Control c, string t)
{
if (c.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SafeSetText);
d.Invoke(d, new object[] { c, t });
}
else
{
c.Text = t;
}
}
The best way to deal with these generic problems is to deal it once. Here I'm posting a small class that wraps the backgroupdworker thread and makes sure that the workcompleted always gets executed on the UI thread.
using System.Windows.Forms;
namespace UI.Windows.Forms.Utilities.DataManagment
{
public class DataLoader
{
private BackgroundWorker _worker;
private DoWorkEventHandler _workDelegate;
private RunWorkerCompletedEventHandler _workCompleted;
private ExceptionHandlerDelegate _exceptionHandler;
public static readonly Control ControlInvoker = new Control();
public DoWorkEventHandler WorkDelegate
{
get { return _workDelegate; }
set { _workDelegate = value; }
}
public RunWorkerCompletedEventHandler WorkCompleted
{
get { return _workCompleted; }
set { _workCompleted = value; }
}
public ExceptionHandlerDelegate ExceptionHandler
{
get { return _exceptionHandler; }
set { _exceptionHandler = value; }
}
public void Execute()
{
if (WorkDelegate == null)
{
throw new Exception(
"WorkDelegage is not assinged any method to execute. Use WorkDelegate Property to assing the method to execute");
}
if (WorkCompleted == null)
{
throw new Exception(
"WorkCompleted is not assinged any method to execute. Use WorkCompleted Property to assing the method to execute");
}
SetupWorkerThread();
_worker.RunWorkerAsync();
}
private void SetupWorkerThread()
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += WorkDelegate;
_worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error !=null && ExceptionHandler != null)
{
ExceptionHandler(e.Error);
return;
}
ControlInvoker.Invoke(WorkCompleted, this, e);
}
}
}
And here is the usage. One thing to note is that it exposes a static property ControlInvoker that needs to be set only once (you should do it at the beginning of the app load)
Let's take the same example that I posted in question and re write it
DataLoader loader = new DataLoader();
loader.ControlInvoker.Parent = this; // needed to be set only once
private void StartButton_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ThreadStart(PerformWorkerTask));
_worker = new BackgroundWorker();
thread1.Start();
}
public void PerformWorkerTask()
{
loader.WorkDelegate = delegate {
// get any data you want
for (int i = 0; i < 10; i++)
{
Thread.Sleep(100);
}
};
loader.WorkCompleted = delegate
{
// access any control you want
MessageLabel.Text = "Completed";
};
loader.Execute();
}
Cheers