Tags:
tech news
How To Do Multithreading And Cross Threading in C# : Template
I wrote this as an example piece for cross-threading operations which allows you to send values to and from non gui threads. It is very well commented and explained, so I will keep the introduction to this piece simple.
Controls and names@
TB1 As TextBox
TB2 As TextBox
listBox1 As ListBox
Button4 As Button
Variable Setters@
// Updates the textbox1 text.
private void UpdateTextB1(string text) //This is the method the callback will work with for TB1.
{
// Set the textbox1 text.
TB1.Text = text;
}
// Updates the textbox2 text.
private void UpdateTextB2(string text) //This is the method the callback will work with for TB2.
{
// Set the textbox text.
TB2.Text = text;
}
// Updates the listbox item.
private void UpdateLB(string text, string text2) //This is the method the callback will work with for ListBox1.
{
// Set the listbox item.
listBox1.Items.Add(text + " " + text2);
}
Button4 Code@
private void button4_Click(object sender, EventArgs e)
{
//At this section we will declare a new thread and provide it with its two parameters. i1 and i2,
//because that is what is required by the NonUIThread void. (You'll see this further down.)
var UodateThread = new Thread(() => NonUIThread(i1, i2)); //Pass the variables to the method.
bgw.RunWorkerAsync(); UodateThread.Start(); //We are simply running both the background worker and
//the non UI thread.
}
Background Worker Do Work Event@
private void bgw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//So we want to access TB1 but its on another thread, (the thread it was probably created on)
//To do this, we will need a delegate called UpdateTextB1Callback, and it requires a method.
//The method we will be using is UpdateTextB1, and it takes one parameter.
TB1.Invoke(new UpdateTextB1Callback(UpdateTextB1),
new string[] { "Text sent on non-UI BG Worker with no variables." });
//Since we know that the control is on another thread and we can't access it, we don't need to
//check with an if statement if the control needs invoking, so we will just invoke it, since we
//know it needs invoking, and checking is not required anyway. I've provided the commented code below
//for reference purposes... to check if a control needs updating, just use TB1 as control example
//and do:
//if (TB1.InvokeRequired)
//{
// TB1.Invoke(:action:);
// //If you uncomment this statement, you will find an error in the code where action is.
// //This is where you must insinuate your own action for that invoke method. Below are two examples.
//}
//else { } //The control doesn't need invoking, do as you please.
}
Non UI Thread and summery comments@
private void NonUIThread(string a, string B )
{
TB2.Invoke(new UpdateTextB2Callback(UpdateTextB2),
new object[] { "Text sent on non-UI thread with variables. "
+ a + " from string i1. And a value of " + b + " from string i2" });
//I would like to point out two things, between these two. The one above is a non-safe method
//because it used an object which is a base type. In codeology, anything can be an object, so it
//can be manipulated. The one below is not exactly the best way to do this either, but its one of
//the most practiced and preferred among some developers, as it does exactly as intended, and its
//the easiest way to pass information between different threads.
listBox1.Invoke(new UpdateLBCallback(UpdateLB),
new string[] { "New", "List Item" });
//Conclusion... You might be wondering how the last bit on line 91, 92 works. But just look at the
//code and you can see how it works, and what the delegate is doing with the method to pass the values.
//I didn't write this as a tutorial. But I just felt like some of it needed commenting for clarity. :)/>/>/>/>/>
//Further to draw a close and some things to try. If you know what your threads are doing and how they
//are accessing your data, then you can use (this NON recommended bool) to skip cross threading issues.
//CheckForIllegalCrossThreadCalls = false; will allow your application to interact without the need
//for the above methods and multi-threading or cross-threading methods. Again, this is not something
//I advocate using, as it can cause monstrous problems on big applications responsible for handing data etc.
//Put at the top of the void "CheckForIllegalCrossThreadCalls = false;" in place of this (Without COMMENTS //):
//TB2.Invoke(new UpdateTextB2Callback(UpdateTextB2),
// new object[] { "Text sent on non-UI thread with variables. "
// + a + " from string i1. And a value of " + b + " from string i2" });
//And above it, put //CheckForIllegalCrossThreadCalls = false; at the top of the void (inside).
//Now execute and you will find you are able to access your controls without restrictions, and this
//is because of the bool we used at the top of the method. I hope this helps some people out there,
//as you could use this as a template to cross-threading and passing values to different threads.
}
Tested and working perfectly on .Net Framework 4.5.2. Class as it stands is an example piece which can be used as a template for cross threading scenarios@
//Lets define our delegates below
public delegate void UpdateTextB1Callback(string text); //For TextBox1 named TB1.
public delegate void UpdateTextB2Callback(string text); //For TextBox2 named TB2.
public delegate void UpdateLBCallback(string string1, string string2); //For ListBox1 named ListBox1.
//Declare two variables, we can use these to pass them between threads.
private string i1 = "String A"; private string i2 = "String B";
// Updates the textbox1 text.
private void UpdateTextB1(string text) //This is the method the callback will work with for TB1.
{
// Set the textbox1 text.
TB1.Text = text;
}
// Updates the textbox2 text.
private void UpdateTextB2(string text) //This is the method the callback will work with for TB2.
{
// Set the textbox text.
TB2.Text = text;
}
// Updates the listbox item.
private void UpdateLB(string text, string text2) //This is the method the callback will work with for ListBox1.
{
// Set the listbox item.
listBox1.Items.Add(text + " " + text2);
}
private void button4_Click(object sender, EventArgs e)
{
//At this section we will declare a new thread and provide it with its two parameters. i1 and i2,
//because that is what is required by the NonUIThread void. (You'll see this further down.)
var UodateThread = new Thread(() => NonUIThread(i1, i2)); //Pass the variables to the method.
bgw.RunWorkerAsync(); UodateThread.Start(); //We are simply running both the background worker and
//the non UI thread.
}
private void bgw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//So we want to access TB1 but its on another thread, (the thread it was probably created on)
//To do this, we will need a delegate called UpdateTextB1Callback, and it requires a method.
//The method we will be using is UpdateTextB1, and it takes one parameter.
TB1.Invoke(new UpdateTextB1Callback(UpdateTextB1),
new string[] { "Text sent on non-UI BG Worker with no variables." });
//Since we know that the control is on another thread and we can't access it, we don't need to
//check with an if statement if the control needs invoking, so we will just invoke it, since we
//know it needs invoking, and checking is not required anyway. I've provided the commented code below
//for reference purposes... to check if a control needs updating, just use TB1 as control example
//and do:
//if (TB1.InvokeRequired)
//{
// TB1.Invoke(:action:);
// //If you uncomment this statement, you will find an error in the code where action is.
// //This is where you must insinuate your own action for that invoke method. Below are two examples.
//}
//else { } //The control doesn't need invoking, do as you please.
}
private void NonUIThread(string a, string B )
{
TB2.Invoke(new UpdateTextB2Callback(UpdateTextB2),
new object[] { "Text sent on non-UI thread with variables. "
+ a + " from string i1. And a value of " + b + " from string i2" });
//I would like to point out two things, between these two. The one above is a non-safe method
//because it used an object which is a base type. In codeology, anything can be an object, so it
//can be manipulated. The one below is not exactly the best way to do this either, but its one of
//the most practiced and preferred among some developers, as it does exactly as intended, and its
//the easiest way to pass information between different threads.
listBox1.Invoke(new UpdateLBCallback(UpdateLB),
new string[] { "New ", "List Item" });
//Conclusion... You might be wondering how the last bit on line 91, 92 works. But just look at the
//code and you can see how it works, and what the delegate is doing with the method to pass the values.
//I didn't write this as a tutorial. But I just felt like some of it needed commenting for clarity. :)/>/>/>/>/>
//Further to draw a close and some things to try. If you know what your threads are doing and how they
//are accessing your data, then you can use (this NON recommended bool) to skip cross threading issues.
//CheckForIllegalCrossThreadCalls = false; will allow your application to interact without the need
//for the above methods and multi-threading or cross-threading methods. Again, this is not something
//I advocate using, as it can cause monstrous problems on big applications responsible for handing data etc.
//Put at the top of the void "CheckForIllegalCrossThreadCalls = false;" in place of this (Without COMMENTS //):
//TB2.Invoke(new UpdateTextB2Callback(UpdateTextB2),
// new object[] { "Text sent on non-UI thread with variables. "
// + a + " from string i1. And a value of " + b + " from string i2" });
//And above it, put //CheckForIllegalCrossThreadCalls = false; at the top of the void (inside).
//Now execute and you will find you are able to access your controls without restrictions, and this
//is because of the bool we used at the top of the method. I hope this helps some people out there,
//as you could use this as a template to cross-threading and passing values to different threads.
}
#endregion Multithreading
}
}