老是忘记,这里做一下笔记
线程
创建一个线程来执行一段代码
class Program { static void Main(string[] args) { Thread thread = new Thread(Run1); thread.Start(); } static void Run1() { Console.WriteLine("子线程Child"+DateTime.Now); } }
创建一个新的线程执行一段代码,这个线程和主线程线程是并行执行的,并不会等待主线程执行完再执行。
class Program { static void Main(string[] args) { // 两个线程是并行执行的 Thread thread = new Thread(Run1); thread.Start(); while (true) { Console.WriteLine("主线程中Main"+DateTime.Now); } } static void Run1() { while (true) { Console.WriteLine("子线程Child"+DateTime.Now); } } }
参数化线程
创建一个线程执行一个方法,给这个方法传值输出,m的值在启用线程之后再次赋值
static void Main(string[] args) { int m = 5; Thread thread = new Thread(() => { Console.WriteLine("m="+m); }); thread.Start(); m = 6; Console.ReadKey(); }
看输入结果是6,这是因为Main函数是由主线程执行的,新创建的thread函数虽然start了,但是合适执行是由操作系统决定,这时候m的值已经赋值成6了之后才执行thread线程,所以输出6
再看一个例子,循环创建10个线程执行输出当前i
static void Main(string[] args) { for (int i = 0; i < 10; i++) { Thread thread1 = new Thread(()=> { Console.WriteLine(i); }); thread1.Start(); } Console.ReadKey(); }
可以看到执行结果并不是按照顺序输出,这是因为创建的线程并不是按照每次循环都会执行,或许循环到5的时候线程执行了两次
所以不建议用以上方法向线程中传参,在线程中传递参数使用ParameterizedThreadStart,避免混乱。
上面的写法改成如下
static void Main(string[] args) { int m = 5; Thread thread1 = new Thread((obj) => { Console.WriteLine(obj); }); thread1.Start(m); m = 6; Console.ReadKey(); for (int i = 0; i < 10; i++) { Thread thread2 = new Thread((item)=> { Console.WriteLine("i="+item); }); thread2.Start(i); } Console.ReadKey(); }
后台线程
创建一个Winform程序,点击按钮执行一段代码
private void button1_Click(object sender, EventArgs e) { // 默认开启的线程都是前台线程,线程没有执行完毕,关闭程序是不会退出的,因为主线程没有关闭 for (int i = 0; i < 100; i++) { Thread thread = new Thread((item) => { //textBox1.Text = item.ToString(); Thread.Sleep(3000); MessageBox.Show(item.ToString()); }); thread.Start(i); } }
启动程序,然后关系Winform程序,会发现程序并不会立即退出,而是等待执行完成才会退出,这是因为,我们创建的线程默认都是前台线程,没有执行完代码程序不退出,
因此;我们只需要把创建的线程设置成后台线程即可,这样关系程序,即使没有执行完,也会立即关闭。
private void button1_Click(object sender, EventArgs e) { // 设置后台线程执行 秒关 for (int i = 0; i < 100; i++) { Thread thread = new Thread((item) => { //textBox1.Text = item.ToString(); Thread.Sleep(3000); MessageBox.Show(item.ToString()); }); thread.IsBackground = true; thread.Start(i); } }
Thread.Sleep()
创建一个线程,先执行一句输出,睡3秒钟再执行,睡的是当前线程所在的代码
static void Main(string[] args) { Thread thread = new Thread(()=> { Console.WriteLine("线程1我要睡了"); Thread.Sleep(3000); Console.WriteLine("线程1我睡醒了"); }); thread.Start(); Console.ReadKey(); }
线程的优先级
创建两个线程,可以设置他们执行的优先级,
t1.Priority = ThreadPriority.Highest; // 优先执行
t2.Priority = ThreadPriority.Lowest; // 最后执行
// 设置优先级后再查看结果 int i = 0, j = 0; Thread t1 = new Thread(() => { while (true) { i++; } }); t1.Priority = ThreadPriority.Highest; // 优先执行 t1.Start(); Thread t2 = new Thread(() => { while (true) { j++; } }); t2.Priority = ThreadPriority.Lowest; t2.Start(); Thread.Sleep(3000); Console.WriteLine("i=" + i + ",j=" + j); Console.ReadKey();
线程同步
创建2个线程t1和t2,同时对一个静态全局变量counter进行加1000 的操作,最后输出counter的值;
while (t1.IsAlive) ;操作会大量消耗 cpu 空转, 可以改成 t1.Join()就是让当前线程等待 t1 线程的结束
class Program { private static int counter = 0; static void Main(string[] args) { Thread t1 = new Thread(() => { for (int i = 0; i < 1000; i++) { counter++; Thread.Sleep(1); } }); t1.Start(); Thread t2 = new Thread(() => { for (int i = 0; i < 1000; i++) { counter++; Thread.Sleep(1); } }); t2.Start(); //while (t1.IsAlive) { } //如果线程存活就一直循环 //while (t2.IsAlive) { } t1.Join(); // 让当前线程等待t1线程执行结束 t2.Join(); Console.WriteLine(counter); Console.ReadKey(); } }
可以看到结果并不是2000,这是因为线程同步问题引起的,当t1线程执行在500 的时候,它要加1,正好t1也执行到500,对counter加了1,这时候counter的值相当于重置成500再加1
使用lock锁来解决
Lock锁---线程同步问题的解决
上面例子问题造成的原因就是因为在同一时刻也有两个线程来访问同一资源,解决以上问题,对要修改的资源加上lock锁锁住即可。注意 lock 要锁定同一个对象,而且必须是引用类型的对象
class Program { private static int counter = 0; private static Object locker = new object(); static void Main(string[] args) { Thread t1 = new Thread(() => { for (int i = 0; i < 1000; i++) { lock (locker) //注意 lock 要锁定同一个对象,而且必须是引用类型的对象 { counter++; } Thread.Sleep(1); } }); t1.Start(); Thread t2 = new Thread(() => { for (int i = 0; i < 1000; i++) { lock (locker) { counter++; } Thread.Sleep(1); } }); t2.Start(); //while (t1.IsAlive) { } //while (t2.IsAlive) { } t1.Join(); // 让当前线程等待t1线程执行结束 t2.Join(); Console.WriteLine(counter); Console.ReadKey(); } }
Abort--线程终止
创建一个线程执行一段耗时的操作,如果想终止可以使用t1..Abort();方法终,但是会引发一个异常,可有用catch捕获处理,参数是ThreadAbortException类型
static void Main(string[] args) { for (int i = 0; i < 40; i++) { try { Thread thread = new Thread((item) => { Console.WriteLine(item); }); thread.Abort(); // 会引发异常 thread.Start(i); } catch (ThreadAbortException ex) { Console.WriteLine(ex); } } }
Interrupt--提前唤醒线程
一个正在线程sleep中,使用Interrupt以可提前唤醒线程,会引发一个异常
static void Main(string[] args) { Thread t1 = new Thread(() => { Console.WriteLine("t1要睡了"); try { Thread.Sleep(5000); } catch (ThreadInterruptedException) { Console.WriteLine("擦,叫醒我干啥"); } Console.WriteLine("t1醒了"); }); t1.Start(); Thread.Sleep(1000); t1.Interrupt(); Console.ReadKey(); }
Join--等待某个线程执行结束
t2线程等待t1线程执行结束后才开始执行
static void Main(string[] args) { Thread t1 = new Thread(() => { for (int i = 0; i < 100; i++) { Console.WriteLine("t1 " + i); } }); t1.Start(); Thread t2 = new Thread(() => { t1.Join();//等着 t1 执行结束 for (int i = 0; i < 100; i++) { Console.WriteLine("t2 " + i); } }); t2.Start(); Console.ReadKey(); }