Threading and cross-threading
-
Hi, I have created a windows form the contains a button and a list box. When the button is clicked it fires the following code,
Migration.Begin(); thread = new Thread(new ThreadStart(Migration.Begin)); thread.Start();
Migration is a seperate class that is executed on a new thread as it will be a labour intensive task and I want the UI to remain usable. However within the migration module I want to be able to call back to the thread that created the UI and have it append a message to the listbox. So intially in the UI class I added a delgate which called a seperate function to add a line to the listbox:public static LoggerListDelegate loggerListDelegate; public Logger() { InitializeComponent(); loggerListDelegate = new LoggerListDelegate(AddLine); }
And the function addline was as follows:public void AddLine(string line) { lstLogger.Items.Add(DateTime.Now.ToLongTimeString() + ": " + line); }
I then called this from the Migration using the following:Logger.loggerListDelegate.BeginInvoke("******Migration has begun******",null,null);
This however is giving me an issue that it is not thread safe as I am trying to invoke on different thread the list was created on. So my understanding is that I would really need to call the Listbox invoke method, e.g. lstLogger.Invoke(delegate, objects) If that's correct how do I access the lstLogger from my Migration class in order to invoke it? If this is not right how do I go about making it threadsafe? Many many many thanks Paul -
Hi, I have created a windows form the contains a button and a list box. When the button is clicked it fires the following code,
Migration.Begin(); thread = new Thread(new ThreadStart(Migration.Begin)); thread.Start();
Migration is a seperate class that is executed on a new thread as it will be a labour intensive task and I want the UI to remain usable. However within the migration module I want to be able to call back to the thread that created the UI and have it append a message to the listbox. So intially in the UI class I added a delgate which called a seperate function to add a line to the listbox:public static LoggerListDelegate loggerListDelegate; public Logger() { InitializeComponent(); loggerListDelegate = new LoggerListDelegate(AddLine); }
And the function addline was as follows:public void AddLine(string line) { lstLogger.Items.Add(DateTime.Now.ToLongTimeString() + ": " + line); }
I then called this from the Migration using the following:Logger.loggerListDelegate.BeginInvoke("******Migration has begun******",null,null);
This however is giving me an issue that it is not thread safe as I am trying to invoke on different thread the list was created on. So my understanding is that I would really need to call the Listbox invoke method, e.g. lstLogger.Invoke(delegate, objects) If that's correct how do I access the lstLogger from my Migration class in order to invoke it? If this is not right how do I go about making it threadsafe? Many many many thanks PaulThere is a huge difference between Control.BeginInvoke and Delegate.BeginInvoke: Delegate.BeginInvoke will call the method on a thread from the ThreadPool, but Control.BeginInvoke will call the method on the thread that runs the message loop for that control (which usually is the main thread). Since the main thread created the form and the controls on it, it is the only thread allowed to access the controls. So you have to use Control.BeginInvoke (lstLogger.BeginInvoke or this.BeginInvoke if "this" is a form).
-
There is a huge difference between Control.BeginInvoke and Delegate.BeginInvoke: Delegate.BeginInvoke will call the method on a thread from the ThreadPool, but Control.BeginInvoke will call the method on the thread that runs the message loop for that control (which usually is the main thread). Since the main thread created the form and the controls on it, it is the only thread allowed to access the controls. So you have to use Control.BeginInvoke (lstLogger.BeginInvoke or this.BeginInvoke if "this" is a form).
Hey Daniel, thanks for a great response, that makes it a lot clearer for me, I was having major mental block! I have just one further question, what is the best way for me to access the lstLogger control from a seperate class? I tried changing the control to public static but the obviously is incorrect. I could in the migration class create a new instance of the Logger class and call it- Logger log = new Logger(); log.lstLogger.BeginInvoke(delegate, objects) But that won't work properly, what are you thoughts? Thanks again Paul
-
Hi, I have created a windows form the contains a button and a list box. When the button is clicked it fires the following code,
Migration.Begin(); thread = new Thread(new ThreadStart(Migration.Begin)); thread.Start();
Migration is a seperate class that is executed on a new thread as it will be a labour intensive task and I want the UI to remain usable. However within the migration module I want to be able to call back to the thread that created the UI and have it append a message to the listbox. So intially in the UI class I added a delgate which called a seperate function to add a line to the listbox:public static LoggerListDelegate loggerListDelegate; public Logger() { InitializeComponent(); loggerListDelegate = new LoggerListDelegate(AddLine); }
And the function addline was as follows:public void AddLine(string line) { lstLogger.Items.Add(DateTime.Now.ToLongTimeString() + ": " + line); }
I then called this from the Migration using the following:Logger.loggerListDelegate.BeginInvoke("******Migration has begun******",null,null);
This however is giving me an issue that it is not thread safe as I am trying to invoke on different thread the list was created on. So my understanding is that I would really need to call the Listbox invoke method, e.g. lstLogger.Invoke(delegate, objects) If that's correct how do I access the lstLogger from my Migration class in order to invoke it? If this is not right how do I go about making it threadsafe? Many many many thanks PaulCan anyone still answer this?
-
Hi, I have created a windows form the contains a button and a list box. When the button is clicked it fires the following code,
Migration.Begin(); thread = new Thread(new ThreadStart(Migration.Begin)); thread.Start();
Migration is a seperate class that is executed on a new thread as it will be a labour intensive task and I want the UI to remain usable. However within the migration module I want to be able to call back to the thread that created the UI and have it append a message to the listbox. So intially in the UI class I added a delgate which called a seperate function to add a line to the listbox:public static LoggerListDelegate loggerListDelegate; public Logger() { InitializeComponent(); loggerListDelegate = new LoggerListDelegate(AddLine); }
And the function addline was as follows:public void AddLine(string line) { lstLogger.Items.Add(DateTime.Now.ToLongTimeString() + ": " + line); }
I then called this from the Migration using the following:Logger.loggerListDelegate.BeginInvoke("******Migration has begun******",null,null);
This however is giving me an issue that it is not thread safe as I am trying to invoke on different thread the list was created on. So my understanding is that I would really need to call the Listbox invoke method, e.g. lstLogger.Invoke(delegate, objects) If that's correct how do I access the lstLogger from my Migration class in order to invoke it? If this is not right how do I go about making it threadsafe? Many many many thanks PaulHi I would do the following: let's say you have 2 classes the worker class
Worker
and the Main FormfrmMain
public class Worker
{
public event EventHandler MyInterThreadEvent;
public Worker()
{
// do something here
}
protected virtual void onMyInterThreadEvent(EventArgs e)
{
if (MyInterThreadEvent != null)
{
MyInterThreadEvent(this, e);
}
}
public void ThreadMethod()
{
do
{
// do something here
onMyInterThreadEvent(EventArgs.Empty);
}while(something);
}
}
public class frmMain:Form
{
private Worker MyPersonalSlave;
// .. All the Form - Stuff coming here...
public frmMain()
{
InitializeComponent();
MyPersonalSlave = new Worker();
MyPersonalSlave.MyInterThreadEvent += new EventHandler(MyPersonalSlave_MyInterThreadEvent);
// start a thread here that makes you worker work.
}
private void MyPersonalSlave_MyInterThreadEvent(object sender, EventArgs e)
{
if (!InvokeRequired) // are we in the correct Thread?
{
// do what ever you want to do when the event gets fired
}
else // use the invoke on the form to run this method in the correct Thread
{
Invoke(new EventHandler(MyPersonalSlave_MyInterThreadEvent),new object[]{sender,e});
}
}
}Greets m@u