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执行这个循环。
最后的结果就是