线程系列01,前台线程,后台线程,线程同步
在控制台应用程序集中,Main方法开始的是一个线程。如果要再创建线程,需要用到System.Threading这个命名空间。
□ 线程是怎样工作的?
CLR维护着一个叫"thread scheduler"的机制,这个机制与操作系统交互。"thread scheduler"保证所有的线程有合适的执行时间,那些等待或被阻塞的线程不会占有CPU时间。在单处理器计算机上,"thread scheduler"在几十毫秒的时间内切换当前线程的执行。在多处理器计算机上,"thread scheduler"一方面也会扮演在单处理器上的角色,此外,还会同时把不同的线程交给不同的CPU处理。值得注意的是:使用多线程并不是总是好的。
□ 线程和进程
一台运行的电脑上可能会有多个进程,一个运行的进程可能会有多个线程。进程之间是完全隔离的,但在同一应用程序中不同线程可以共享数据。线程之间可以协同工作,比如一个后台线程用来获取数据,当获取到数据之后,另一个线程用来显示数据。
□ 多线程的应用场景
○ 保持一个更快响应的UI界面:让UI线程只处理鼠标和键盘触发事件,其它线程处理其它事件。
○ 有效利用CPU:一个线程被阻塞,该线程就不会占有CPU资源。
○ 并行编程:执行一个比较耗时的任务时,可以分摊给多个线程。
○ 投机执行:让一个有可能被执行的线程先提前执行;让不同的线程用不同的算法,最先得出结果的胜出。
○ 请求被同步处理:无论是在客户端还是在服务端,可以用多线程同时处理多个并发请求。
□ 创建第一个线程
using System;
using System.Threading;
namespace ConsoleApplication4
{class Program
{static void Main(string[] args){var thread = new Thread(DoSth);
thread.Start();}static void DoSth(){Console.WriteLine("我来自另外一个线程");
}}}
实际上,new Thread(ThreadStart del)中的形参是委托类型:
public delegate void ThreadStart();
既然是委托,那在声明委托的时候,同样可以使用委托的构造函数:
var thread = new Thread(new ThreadStart (DoSth))
如果DoSth方法带参数,可以这样写:
var thread = new Thread(new ParameterizedThreadStart(DoSth));
□ 前台线程和后台线程
新建的线程在默认情况下是前台线程,可以通过把IsBackground属性设置为true,把线程定义为后台线程,一旦定义成后台线程,只要前台线程结束,无论后台线程是否结束,应用程序进程结束。
using System;
using System.Threading;
namespace ConsoleApplication4
{class Program
{static void Main(string[] args){Console.WriteLine(Thread.CurrentThread.ManagedThreadId);var thread = new Thread(DoSth);
thread.IsBackground = true;
thread.Start(1);Console.WriteLine("离开主线程");
}static void DoSth(object threadId){Console.WriteLine("我来自另外一个线程" + threadId);
}}}
○ ManagedThreadId属性,托管线程Id,在进程内唯一,与操作系统的线程Id不是一回事。
○ Start方法可以带参数,参数将被传递到线程方法
○ IsBackground属性,设置线程是否为后台线程
默认情况下,创建的线程都是前台线程,只要有一个前台线程在运行,运用程序就不会停止;如果有些线程的是后台线程,当所有的前台线程运行完毕,应用程序就会停止,所有的后台线程也必须同时被终止。
□ 线程同步
※ 线程不同步的问题
假设主程序中有一个静态变量,在主程序的方法内无限循环,每次让该静态变量自增1。
如果把该方法交给一个线程。
class Program
{private static int count = 0;static void Main(string[] args){var t1 = new Thread(AddCount);
t1.Start();}static void AddCount(){while (true){int temp = count;
Thread.Sleep(1000);count = temp + 1;Console.WriteLine("我的托管线程ID为:" + Thread.CurrentThread.ManagedThreadId + " 目前count的值为:" + count);Thread.Sleep(1000);}}}
运行良好,显示的count值是连续递增1。
如果把该方法交给2个线程。
class Program
{private static int count = 0;static void Main(string[] args){var t1 = new Thread(AddCount);
var t2 = new Thread(AddCount);
t1.Start();t2.Start();}......}
我们发现,count的值不是递增。也就是说,count的值没有做到同步。
→进入线程1,temp=0,线程1开始sleep
→进入线程2,temp=0,线程2开始sleep
→线程1"醒来",让count=1,显示count值为1,又sleep
→线程2"醒来",temp还是为0,所以count还是为1,显示count值为1,又sleep
→如此循环
这里的问题是:本想让count一直递增,但线程1和线程2没有适时同步。如何解决呢?
※ 让线程同步
使用lock语句块,可以让2个线程同步,让每次只有一个线程进入程序执行的某个部分。
class Program
{private static int count = 0;static object o = new object();static void Main(string[] args){var t1 = new Thread(AddCount);
var t2 = new Thread(AddCount);
t1.Start();t2.Start();}static void AddCount(){while (true){lock (o)
{int temp = count;
Thread.Sleep(1000);count = temp + 1;Console.WriteLine("我的托管线程ID为:" + Thread.CurrentThread.ManagedThreadId + " 目前count的值为:" + count);}Thread.Sleep(1000);}}}
总结:
○ 如果允许一个主线程结束,其它线程不管执行情况如何都结束,就把其它线程设置为后台线程。
○ lock语句块能保证线程同步
线程系列包括: