Starting a new thread using the constructor
I finished my little project that makes the hard climb. I realized that in this short computation time my GUI freezes up. So I did some research and I found this => http://www.codeproject.com/Articles/4381/Threading-out-tasks-in-aC-NET-GUI
I started to implement this in my project, but I realized that this particular implementation was not working in my project.
In my project I have many classes and one "manager" that controls all the other classes. If I initialize this Manager class, it already does the heavy lifting in the constructor.
To my question:
How to start a new thread using a countermeasure?
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: ok, I decided to recode my program. now a heavy lift 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
}
}
works sl.Invoke(new MethodInvoker(mgr.test)); // first try
, but it still hangs on 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();
}
source to share
To do this, you must take a different approach. Your constructor is still called 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();
}));
}
}
source to share
Move the "heavy lift" from the constructor to some "worker" and run that method on a thread.
Change the dispatcher:
public Manager(/*params*/)
{
//params
//heavy lifting
}
to
public Manager(/*params*/)
{
//params
}
public void DoWork()
{
//heavy lifting
}
and call
Manager mgr = new Manager(e, handreader);
Thread mgrThread = new Thread(new ThreadStart(mgr.DoWork));
mgrThread.Start();
WARNING: If you are accessing / modifying UI elements on the thread, remember to make calls!
source to share
Well, you could use:
Thread mgrThread = new Thread(() => new Manager(e, handreader));
... but then you won't have a manager reference for the rest of your code.
To be honest, doing heavy lifting in a constructor is usually a bad idea for a variety of reasons. It would be better to move this work elsewhere:
// Constructor just sets things up
Manager mgr = new Manager(e, handreader);
// DoWork method does the real work
Thread mgrThread = new Thread(mgr.DoWork);
source to share