1.線程的用法
無參數的線程:
1 private void button1_Click(object sender, EventArgs e) 2 { 3 Thread thread1 = new Thread(new ThreadStart(fun)); 4 thread1.Start(); 5 } 6 7 private void fun() 8 { 9 int a = 0; 10 for (int i = 0; i < 999999999; i++) 11 { 12 a = i; 13 } 14 MessageBox.Show(a.ToString()); 15 }
帶參數的線程:
第一種方法:使用ParameterizedThreadStart。
1 private void button2_Click(object sender, EventArgs e) 2 { 3 Thread thread1 = new Thread(new ParameterizedThreadStart(fun1)); 4 //Thread thread1 = new Thread(fun1);//這樣寫也可以 5 thread1.Start("123"); 6 7 List<string> list = new List<string> { "張三", "李四", "王五" }; 8 Thread thread2 = new Thread(new ParameterizedThreadStart(fun2)); 9 thread2.Start(list); 10 } 11 12 private void fun1(object obj) 13 { 14 //注意參數必須為object類型 15 MessageBox.Show(obj.ToString()); 16 } 17 18 private void fun2(object obj) 19 { 20 List<string> list = obj as List<string>; 21 for (int i = 0; i < list.Count; i++) 22 { 23 MessageBox.Show(list[i]); 24 } 25 }
第二種方法:將線程執行的方法和參數都封裝到一個類里面。通過實例化該類,方法就可以調用屬性來實現間接的類型安全地傳遞參數。
1 private void button3_Click(object sender, EventArgs e) 2 { 3 NewThread newThread = new NewThread("張三");//實例化對象 4 Thread thread = new Thread(new ThreadStart(newThread.fun)); 5 thread.Start(); 6 7 List<string> list = new List<string> { "張三", "李四", "王五" }; 8 Thread thread2 = new Thread(new ParameterizedThreadStart(newThread.fun1)); 9 thread2.Start(list); 10 }
1 public class NewThread 2 { 3 private string name; 4 public NewThread(string name) 5 { 6 this.name = name; 7 } 8 public void fun() 9 { 10 MessageBox.Show(string.Format("我的名字叫{0}", name)); 11 } 12 public void fun1(object obj) 13 { 14 List<string> list = obj as List<string>; 15 for (int i = 0; i < list.Count; i++) 16 { 17 MessageBox.Show(list[i]); 18 } 19 } 20 }
2.IsBackground:獲取一個值,是否為后台線程。true為后台線程,false為前台線程,默認為前台線程。
前台線程和后台線程的區別:前台線程是應用程序必須運行完所有線程才可以退出。后台的則直接退出。
1 private void button1_Click(object sender, EventArgs e) 2 { 3 Thread thread1 = new Thread(new ThreadStart(fun)); 4 thread1.IsBackground = true;//后台線程 5 thread1.Start(); 6 } 7 private void fun() 8 { 9 int a = 0; 10 for (int i = 0; i < 999999999; i++) 11 { 12 a = i; 13 } 14 MessageBox.Show(a.ToString()); 15 }
運行結果:當你關閉button1后立即關閉窗口,則整個程序退出,就不會彈窗了。
如果不設置thread1.IsBackground或者設置為false,當你關閉窗口后,應用程序應在執行,結果還會彈窗。
3.CheckForIllegalCrossThreadCalls和Invoke或BeginInvoke的用法。
這種異常的解決辦法:
第一種:CheckForIllegalCrossThreadCalls=false;不對它進行線程檢查(這種方法雖然簡單,但不提倡用)
1 public partial class Form1 : Form 2 { 3 public Form1() 4 { 5 InitializeComponent(); 6 TextBox.CheckForIllegalCrossThreadCalls = false;//是否對該控件進行線程檢查。 7 } 8 9 private void button1_Click(object sender, EventArgs e) 10 { 11 Thread thread1 = new Thread(new ThreadStart(fun)); 12 thread1.IsBackground = true; 13 thread1.Start(); 14 } 15 16 private void fun() 17 { 18 for (int i = 0; i < 2000; i++) 19 { 20 int a = Convert.ToInt32(textBox1.Text); 21 a++; 22 textBox1.Text = a.ToString(); 23 } 24 } 25 }
第二種:Invoke和BeginInvoke
Control.Invoke 方法 (Delegate) :在擁有此控件的基礎窗口句柄的線程上執行指定的委托(是同步執行的,會阻塞當前線程,只允許單線程修改)。
Control.BeginInvoke 方法 (Delegate) :在創建控件的基礎句柄所在線程上異步執行指定委托(會啟用線程池,是異步的不會阻塞線程)。
1 private void button1_Click(object sender, EventArgs e) 2 { 3 Thread thread3 = new Thread(new ThreadStart(doWork)); 4 thread3.IsBackground = true; 5 thread3.Start(); 6 } 7 public delegate void MyInvoke(); 8 public void doWork() 9 { 10 MyInvoke callBack = new MyInvoke(fun3); 11 //this.BeginInvoke(callBack); 12 this.Invoke(callBack); 13 } 14 private void fun3() 15 { 16 for (int i = 0; i < 2000; i++) 17 { 18 int a = Convert.ToInt32(textBox1.Text); 19 a++; 20 textBox1.Text = a.ToString(); 21 } 22 }
4.lock的用法
textBox1.Text的初始值為textBox1.Text="0";
大家猜下當點擊Button時這個textBox1.Text的值會是多少呢?
1 public Form1() 2 { 3 InitializeComponent(); 4 TextBox.CheckForIllegalCrossThreadCalls = false;//是否對蓋控件進行線程的調用。 5 } 6 7 private void button1_Click(object sender, EventArgs e) 8 { 9 Thread thread1 = new Thread(new ThreadStart(fun)); 10 thread1.IsBackground = true; 11 thread1.Start(); 12 13 Thread thread2 = new Thread(new ThreadStart(fun)); 14 thread2.IsBackground = true; 15 thread2.Start(); 16 } 17 18 private void fun() 19 { 20 for (int i = 0; i < 2000; i++) 21 { 22 int a = Convert.ToInt32(textBox1.Text); 23 a++; 24 textBox1.Text = a.ToString(); 25 } 26 }
結果:
為什么會是一個隨機的數呢?而不是2000或者4000?
因為當你在點擊Button時,先開啟thread1然后再開啟thread2,但是都在執行着那個方法。在開啟thread2的時候,文本框內的值可能不是thread2線程上次的值。
例如:thread1在某次執行時,文本框里的值為1000,thread2上次執行的結果為999,它在這次執行時就有可能取thread1執行后的值1000來計算。這時文本框就為1001了。
所以整個下來,就會產生一個隨機的數字了。
解決辦法:
1 private void fun() 2 { 3 lock (this) 4 { 5 for (int i = 0; i < 2000; i++) 6 { 7 int a = Convert.ToInt32(textBox1.Text); 8 a++; 9 textBox1.Text = a.ToString(); 10 } 11 } 12 }
就是在方法中加上一個lock鎖。當thread1在執行時,不讓thread2執行這個循環。
最后的結果就是