Starting a new thread with a constructor - c#

I finished my little project that does some heavy lifting. i realized in this short calculation time, my GUI freezes. So I did some research and I found this => http://www.codeproject.com/Articles/4381/Threading-out-tasks-in-a-C-NET-GUI
I started to implement this is my project, but i realized that this particular implementation does not work in my project.
In my project i have many classes and one " manager " that controls all other classes. If i initilize this Manager class , it already does the heavy lifting in the constructor.
To my Question :
How do i start a new thread with a contructor ?
private void fileWatcher_Changed(object sender, System.IO.FileSystemEventArgs e)
{
if (System.IO.File.Exists(e.FullPath) == true)
{
Manager mgr = new Manager(e, handreader); // here starts the heavy lifting
Thread mgrThread = new Thread(new ThreadStart(mgr)); // what to do ?
sl.Text = mgr.test();
txtLog.Text = mgr.output();
}
}
EDIT :
okay i decided to recode my program. now the heavy lifting is in one function but i think i made a mistake.
the whole program looks like this :
private void fileWatcher_Changed(object sender, System.IO.FileSystemEventArgs e)
{
if (System.IO.File.Exists(e.FullPath) == true)
{
Manager mgr = new Manager(e, handreader, txtLog, sl);
//sl.Invoke(new MethodInvoker(mgr.test));
sl.Invoke(new MethodInvoker(mgr.test)); // first try
Thread mgrThread = new Thread(new ThreadStart(mgr.test)); // second try
}
}
the sl.Invoke(new MethodInvoker(mgr.test)); // first try works but it still freezes my GUI.
Thread mgrThread = new Thread(new ThreadStart(mgr.test)); // second try
and this line does nothing.
my test function :
public void test()
{
StringBuilder builder = new StringBuilder();
foreach (PlayerController pc in fm.lPc)
{
Range range = new Range(handReader.hand, handReader.handversus, pc);
builder.Append(pc.getHeroCardsSimple()+" vs 100% range = "+range.vsRange()+"\r\n");
}
sl.Text = builder.ToString();
}

You should use a different approach for this. Your constructor is still being invoked on the GUI thread.
   
Func<Manager> asyncConstructor;
private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
{
asyncConstructor = new Func<Manager>(() => new Manager());
asyncConstructor.BeginInvoke(ManagerConstructed, null);
}
private void ManagerConstructed(IAsyncResult result)
{
Manager mgr = asyncConstructor.EndInvoke(result);
//we can only access form controls from the GUI thread,
//if we are not on the gui thread then
//do the changes on the gui thread.
if (this.InvokeRequired)
{
this.Invoke(new Action(() =>
{
sl.Text = mgr.test();
txtLog.Text = mgr.output();
}));
}
}

Move the "heavy lifting" out of the constructor to some kind of "worker" and run that method in the thread.
Change the Manager from:
public Manager(/*params*/)
{
//params
//heavy lifting
}
to
public Manager(/*params*/)
{
//params
}
public void DoWork()
{
//heavy lifting
}
and the calling to
Manager mgr = new Manager(e, handreader);
Thread mgrThread = new Thread(new ThreadStart(mgr.DoWork));
mgrThread.Start();
ATTENTION: If you access/change UI elements in the thread, don't forget to Invoke that calls!

Well, you could use:
Thread mgrThread = new Thread(() => new Manager(e, handreader));
... but then you won't have a reference to the manager for the rest of your code.
To be honest, doing the heavy lifting in the constructor is generally a bad idea anyway, for various reasons. It would be better to move that work somewhere else:
// Constructor just sets things up
Manager mgr = new Manager(e, handreader);
// DoWork method does the real work
Thread mgrThread = new Thread(mgr.DoWork);

Related

When I use cross thread then my application doesn't respond until background operations complete

I'm working on windows from application in .net framework 2.0.
There is some operations run in background like database backup, progress bar and label text update etc.
But When I use cross thread then my application doesn't respond(busy icon) until background operations complete
This is example code
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(UpdateInfo));
t.Start();
}
private void UpdateInfo()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(UpdateInfo));
}
else
{
// send query to database here for taking backup that could take time
// update progress bar
//I'm also using sqlconnection InfoMessage here
label1.Text = "Text upading......
}
}
private void OnInfoMessage(sender As Object, e As SqlInfoMessageEventArgs)
{
}
Scenario:
Scenario is user could cancel operation but it can't due to application not responding
================Update Code==========================================
My Code is like
private void btnBackup_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(MyThreadFunc));
t.Start();
}
public void MyThreadFunc()
{
if (this.InvokeRequired) {
this.Invoke(new MethodInvoker(Backup));
} else {
Backup();
}
}
public void Backup()
{
string databaseName = cbDatabase.Text;// getting the name of database for backup
SaveFileDialog1.ShowDialog(); // dialog will open
string backupFileName = SaveFileDialog1.FileName; // getting location of backup
//============ database query==================
SqlConnection con = new SqlConnection(conString);
con.FireInfoMessageEventOnUserErrors = true;
con.InfoMessage += OnInfoMessage;
con.Open();
query = string.Format("backup database {0} to disk = {1}", databaseName,backupFileName);
using (cmd == new SqlCommand(query, con)) {
cmd.CommandTimeout = 0;
cmd.ExecuteNonQuery();
}
con.Close();
con.InfoMessage -= OnInfoMessage;
con.FireInfoMessageEventOnUserErrors = false;
//============ Database operation end==================
}
private void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
{
lblStatusMsg.Text = e.Message; // mostly messages are like. 1 percent complete, 5 percent complete, 11 percent complete
foreach (SqlError info in e.Errors) {
if (info.Class > 10) {
// errror logging
} else {
Regex reger = new Regex("\\d+");
Match regerMatch = reger.Match(e.Message);
if (ProgressBar1.Value == 100) {
} else {
ProgressBar1.Value = regerMatch.Value;
}
}
}
}
Not responding issue until database operation completes
The purpose of the Invoke call is to have code run on the main thread. Your code is therefore creating a thread whose entire purpose is to force the main thread to run all the code.
Let's assume that you want to run a thread that, 10 seconds after it starts, updates a label's text to indicate completion. You still need to Invoke the label update, but that's the only thing that should be in the invoke.
In that case your thread function should look something like this:
private void MyThreadFunc()
{
// do something here
Thread.Sleep(10000);
// update the label:
if (label1.InvokeRequired)
Invoke(UpdateLabel);
else
UpdateLabel();
}
private void UpdateLabel()
{
label1.Text = "Something was finished.";
}
In other words, you need to separate out those things that have to run on the main thread (like anything that updates controls on your form) and Invoke only those bits. The rest of it should happen outside of the Invoke.
I guess I didn't make it clear.
The Invoke method is used to execute code in the context of the thread that owns the handle of the control or form that you're invoking on. You can use this to interact with controls on the UI, but you should only use it for that purpose. If you put all of the thread's close in an Invoke call then all of the thread's code will run in the UI thread, which makes it completely pointless to have a separate thread.
If you want to stop your application's UI from pausing while things happen - which is, after all, one of the main reasons to use a thread - then you should use the Invoke method only when absolutely necessary, and then only for very small sections of code. Call Invoke to update a control's parameters, interact with the non-threadsafe properties of the form, etc. You can use dialog boxes and so on directly from your other thread, although some prefer to use Invoke for those as well.
And if you're doing multiple invokes then you probably should write some helper methods to wrap the Invoke to clean things up. Something like:
public void Invoker(Action action)
{
if (InvokeRequired)
Invoke(action);
else
action();
}
public T Invoker<T>(Func<T> func)
{
if (InvokeRequired)
return (T)Invoke(func);
else
return func();
}
Now you can write your thread code with minimal impact like this:
public void ThreadFunc()
{
System.Threading.Thread.Sleep(1000);
Invoker(() => this.label1.Text = "Started");
for (int i = 1; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
Invoker(() => this.label1.Text = string.Format("Iteration {0}", i));
}
System.Threading.Thread.Sleep(1000);
Invoker(() => this.label1.Text = "Completed");
}
Or if you don't like lambda functions (for some reason) you can use methods like this:
public void Invoker<T>(Action<T> action, T p)
{
if (InvokeRequired)
Invoke(action, p);
else
action(p);
}
private void SetLabel(string value)
{
label1.Text = value;
}
And then in your code:
Invoker(SetLabel, "new text value");
The important part is to keep the code you're invoking be tiny or you'll end up blocking your main thread.

Progress bar (spinner) not animated when background thread is working

I have a background worker that does some work. I want to have a spinner on main control indicating that app is working. But looks like threading is preventing my spinner from animating (sometimes it doesnt even show)... can some one explain why it is not working (probably because sleeping the thread) and perhaps guide me to a solution with minimal code changes :)
Best regards, no9.
public void StartProcess(object obj)
{
this.eventAggregator.GetEvent<ActionEvent>().Publish(new Message(EMessageType.Info)
{
Title = "Start",
Description = "Starting action..."
});
Worker = new BackgroundWorker();
Worker.DoWork += new DoWorkEventHandler(worker_DoWork);
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
try
{
this.Document = null;
Dictionary<string, Stream> tmp = this.GetContent();
//start and show the spinner
this.View.ShowDocumentProgressSpinner(true);
Worker.RunWorkerAsync(tmp);
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "LogAndSwallow");
this.eventAggregator.GetEvent<ActionEvent>().Publish(new Message(EMessageType.Error)
{
Title = "Error",
Description = "There was an error processing your action."
});
}
finally
{
this.View.ShowActionButton(false);
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
var logger = new ActionLoggerAndViewUpdater(this.eventAggregator, this.View);
foreach (KeyValuePair<string, Stream> pair in (Dictionary<string, Stream>)e.Argument)
{
using (Stream stream = pair.Value)
{
//setting the document fires login event that changes stuff on presenter (current class instance)
this.Document = new Document(stream);
//check if ok to continue
while (!this.IsLoggedInForTheWorkingDocument)
//wait of the login stuff to complete
Thread.Sleep(2000);
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
//this depends on the login and takes some time to process
this.DoSomeStuff();
}));
}
}
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//stop and hide the spinner
this.View.ShowDocumentProgressSpinner(false);
...
}
Why do you invoke something on your background worker? The background workers whole purpose is to not run in the UI thread and block it. Do not invoke a long running process from the worker, it defeats it's purpose.
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
var logger = new ActionLoggerAndViewUpdater(this.eventAggregator, this.View);
foreach (KeyValuePair<string, Stream> pair in (Dictionary<string, Stream>)e.Argument)
{
using (Stream stream = pair.Value)
{
this.Document = new Document(stream);
// this should REALLY be handled by an event, not busy waiting:
while (!this.IsLoggedInForTheWorkingDocument)
//wait of the login stuff to complete
Thread.Sleep(2000);
// removed the invoking, this is supposed to run in the background, right?
this.DoSomeStuff();
}
}
}
Because your worker runs in another thread, it cannot update the UI thread as it progresses, hence the behaviour. See this other, similar question Updating GUI (WPF) using a different thread.
Essentially you need to call control.Dispatcher.Invoke to get your UI to update from the other thread

How to execute method calls of a custom class in a separate thread?

I have an UI, a custom class, and a thread. I want to run the custom class completely in a separate thread. Is there a clean way of doing this?
For example. On the MainForm below, when UI calls _threadOneClass.Sleep, I need the UI to go to the spawned ThreadOne and invoke the Sleep method in ThreadOne, not in the main thread.
Basically, all method calls in MyClass need to be executed in ThreadOne, not in main thread. It is like, the MyClass runs on its own "process", while still visible to be called from MainForm.
The MainForm has 3 buttons, and 1 textbox for logging.
I was thinking of deriving the Thread class, but it is sealed. So deriving is definitely a wrong way per Microsoft.
Help dear experts?
Here is the output (MainThread ID=10, ThreadOne ID=11)
MyClass instantiated
Starting ThreadOne
11-Run.start
Sleeping ThreadOne
10-Run.sleep for 3000 'Need this to run on ThreadID 11
10-Run.woke up 'Need this to run on ThreadID 11
Stopping ThreadOne
11-Run.done
Here is how the code look like.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private Thread _threadOneThread;
private MyClass _threadOneClass;
private void btnThreadOneCreate_Click(object sender, EventArgs e)
{
_threadOneClass = new MyClass(this);
_threadOneThread = new Thread(new ThreadStart(_threadOneClass.Run));
_threadOneThread.Start();
}
private void btnThreadOneStop_Click(object sender, EventArgs e)
{
_threadOneClass.IsRunning = false;
}
private void btnThreadOneSleep_Click(object sender, EventArgs e)
{
_threadOneClass.Sleep(3000);
}
public void Log(string txt)
{
MainForm.SetText(txtLog, txt);
}
internal static void SetText(Control ctl, string val)
{
if (ctl.InvokeRequired)
ctl.Invoke((MethodInvoker)delegate() { ctl.Text += Environment.NewLine + val; });
else
ctl.Text += Environment.NewLine + val;
}
}
class MyClass
{
public MyClass(MainForm frm)
{
_mainForm = frm;
}
private MainForm _mainForm;
public bool IsRunning = true;
public void Run()
{
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.start");
while (IsRunning) { }
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.done");
}
public void Sleep(int milliseconds)
{
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.sleep for " + milliseconds.ToString());
Thread.Sleep(milliseconds);
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.woke up");
}
}
Threads allow you to run heavy operations while you continue doing other things. In the case of user interfaces (your scenario), asynchronous behavior is almost always necessary as blocking the UI thread will cause to be unresponsive to the user and just isn't an option.
Luckily, the folks at Microsoft has made it extremely easy to write the same code, but in an asynchronous manner. I usually use Tasks because I like the control you get over the operation as well as the ContinueWith() lets you control what you do with the result should you need to propagate data back to the calling thread. If you prefer to use threads, ThreadPool.QueueUserWorkItem is just as easy.
Any operation you do not want to block the UI thread wrap it like this,
Task.Factory.StartNew(() => Object.PerformOperation());
or
ThreadPool.QueueUserWorkItem(new WaitCallback((x) => Object.PeroformOperation()));
I find this allows me to write the same exact code, but without blocking the UI thread. If you have several statements to execute you can use a block as well.
Task.Factory.StartNew(() =>
{
// do something
// do more stuff
// done
}).ContinueWith((completedTask) =>
{
// if you were computing a value with the task
// you can now do something with it
// this is like a callback method, but defined inline
// use ui's dispatcher if you need to interact with ui compontents
UI.Label.Dispatcher.Invoke(new Action(() =>
UI.Item.Label.Text = completedTask.Result;
}
The upcoming async features that are being released in the next .net version actually streamline this even more! But since it uses tasks you will still want to get comfortable with using them.
// this will begin the operation, then return control back to the ui so it does not hang.
var result = await Object.PerformLongTask();
// once the long task is completed then it continues and you can use the result
UI.Item.Label = result;
To give a real example, here is some code from an FTP client I wrote which has has a WPF front end. When the start button is clicked the ftp transfer is launched in it's own task, then a while loop which updates the interface every half a second is launched in a task, so neither interferes with the interface thread. Again it's the same code, just wrapped in lambada's.
private void btnStart_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
ftp.Mirror(#"C:\LocalFolder", "/RemoteFolder", 10));
Task.Factory.StartNew(() =>
{
while (true)
{
lbPercentSuccess.Dispatcher.Invoke(new Action(() =>
{
lbPercentSuccess.Content = ftp.FtpProgress.SuccessPercentage;
lbPercentError.Content = ftp.FtpProgress.ErrorPercentage;
lbPercentTotal.Content = ftp.FtpProgress.TotalPercentage;
lbDuration.Content = ftp.FtpProgress.Duration;
}));
Thread.Sleep(500);
}
});
}
This is not possible to my knowledge. You can only run and invoke individual methods or queue them on separate threads when need be. Setting an actual object on a separate thread defeats your purpose. This is because you only going to harness the benefits of multithreading when invoking a method on a separate thread not an object.
then reassign the del to MethodTwo... and so on. This is made easier if you conform to a method signature.
Possible solution:
Thread threadTest = new Thread(new ThreadStart(MethodOne));
threadTest = new Thread(new ThreadStart(MethodTwo));
threadTest.Start();
Or
Action del = TestClass.MethodOne;
IAsyncResult result = del.BeginInvoke(null, null);
Func<int,int> del = TestClass.MethodOne;
IAsyncResult result = del.BeginInvoke(11,null, null);
int value = del.EndInvoke(result);
It's not simple, but have a look at this. It's a nice explination of how to use cross thread communication.
http://www.codeproject.com/KB/cs/delegatequeue.aspx
So far, this is what I found (from iPhone development). The Run loop acts like a spine that invokes various methods. It is implemented like the following:
A more elegant solution is welcomed.
class MyClass
{
public MyClass(MainForm frm)
{
_mainForm = frm;
}
private MainForm _mainForm;
public bool IsRunning = true;
public void Run()
{
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.start");
while (IsRunning)
{
if (_runSleepMilliSeconds != null)
{
_Sleep(_runSleepMilliSeconds ?? 3000);
_runSleepMilliSeconds = null;
}
}
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.done");
}
private int? _runSleepMilliSeconds = null;
public void Sleep(int milliseconds)
{
_runSleepMilliSeconds = milliseconds;
}
private void _Sleep(int milliseconds)
{
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.sleep for " + milliseconds.ToString());
Thread.Sleep(milliseconds);
_mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.woke up");
}
}

Best pattern for "Do some work and quit"

I'm currently writing a little GUI program that does some work and exits afterwards. While work is done, the GUI thread is updated with infos for the user.
This is the pattern I'm currently using and I'm thinking it's not the most elegant one:
static void MainForm_Loaded(BeoExport exporter)
{
// Thread 1 runs the Export
workerThread = new Thread(() =>
{
exporter.StartExport();
// don't exit immediately, so the user sees someting if the work is done fast
Thread.Sleep(1000);
});
// Thread 2 waits for Thread 1 and exits the program afterwards
waiterThread = new Thread(() =>
{
workerThread.Join();
Application.Exit();
});
workerThread.Start();
waiterThread.Start();
}
So what pattern/mechanics would you use to do the same?
To clarify: I was not interested in a way to update the GUI thread. That's already done. This might sound esoteric but I was lookig for the right way to quit the application.
If I could, I would give Dave the credits, since he pointed out the usefulness of the BackgroundWorker.
Have you considered a BackgroundWorker thread instead? You can use its ReportProgress method and ProgressChanged event to update the GUI (with a progress bar perhaps), assuming that you can refactor BeoExport.StartExport method to also report progress. This gives the users visible feedback that work is actually happening.
I don't understand why do you use two threads. You can use threadpool:
ThreadPool.QueueUserWorkItem((state)=>{
exporter.StartExport();
Thread.Sleep(1000);
Application.Exit();
});
I suggest you to use the BackgroundWorker class. It's thought to do the kind of job you're doing. You could do domething like this:
public class Form1 : Form
{
private BackgroundWorker worker;
private ProgressBar bar;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
bar= new ProgressBar();
bar.Dock = DockStyle.Top;
Controls.Add(bar);
worker = new BackgroundWorker();
worker.WorkerReportsProgress=true;
worker.RunWorkerCompleted += delegate
{
Close();
};
worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs ev)
{
bar.Value = ev.ProgressPercentage;
};
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//do your work here. For the example, just sleep a bit
//and report progress
for (var i = 0; i < 100;i++ )
{
Thread.Sleep(50);
worker.ReportProgress(i);
}
}
}
You can use an AutoResetEvent. The main thread waits for the autoreset event to be reset.
var wh = new AutoResetEvent(false);
var workerThread = new Thread(() =>
{
exporter.StartExport();
// don't exit immediately, so the user sees something if the work is done fast
Thread.Sleep(5000);
wh.Set();
});
workerThread.Start();
wh.WaitOne();
Application.Current.Shutdown();
Have you taken a look at the Task Parallel Library in .net 4 you can set up a task and the library will work out to best pararellise it for you, either threading, working a seperate CPU core's the is a load of great information about it online.
Regards
Iain
To add a little to Lain's answer, here's a Console sample using a Task from the System.Threading.Tasks namespace.
class Program
{
static void Main(string[] args)
{
Task<int> task = Task<int>.Factory.StartNew(() =>
{
Exporter exporter = new Exporter();
int i = exporter.StartExport();
return i;
});
int iResult = task.Result;
Console.WriteLine(iResult);
Console.ReadLine();
}
class Exporter {
public int StartExport()
{
//simulate some work
System.Threading.Thread.Sleep(500);
return 5;
}
}
}
Using a BackgroundWorker might help you implement your background processing. If you wanted to stick with your current pattern then consider the following.
static void MainForm_Loaded(BeoExport exporter)
{
workerThread = new Thread(() =>
{
exporter.StartExport();
Thread.Sleep(1000);
MainForm.BeginInvoke(
(Action)(() =>
{
MainForm.Close();
});
});
workerThread.IsBackground = true;
workerThread.Start();
}
Have the worker thread send a notification message of some description to the main thread. The GUI can then either exit or display a "done" message as appropriate.

BackgroundWorkerThread access in a thread

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

Categories