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 } }