# 进程与线程的概念 - 进程可以理解为一块包含了某些资源的内存区域,操作系统通过进程这一方式把它的工作划分为不同的单元。一个应用程序可以对应于多个进程。 - 线程是进程中的独立执行单元,对于操作系统而言, 它通过调度线程来使用应用程序工作,一个进程中至少包含一个线程,我们把该线程成为主线程。 - 线程与进程之间的关系可以理解:线程是进程的执行单元,操作系统通过调度线程来使应用程序工作;而进程则是线程的容器,它由操作系统创建,又在具体的执行过程中创建了线程。 # 线程也分前后台 - 线程有前台线程和后台线程之分。在一个进程中,当所有前台线程停止运行后,CLR会强制结束所有仍在运行的后台线程,这些后台线程被直接终止,却不会抛出任何异常。主线程将一直是前台线程。 ``` 可以使用Tread类来创建前台线程 using System; using System.Threading; namespace 多线程1 { internal class Program { private static void Main {string[] args} { var backThread =new Thread(Worker); backThread.IsBackground=true; backThread.Start(); Console.WriteLine("从主线程退出"); Console.ReadKey(); } private static void Worker() { Thread.Sleep(1000); Console.WriteLine("从后台线程退出"); } } } 通过Thread 类创建了一个线程对象,然后通过设置IsBackground属性来指明该线程为后台线程。如果不设置这个属性,则默认为前台线程。接着调用Start的方法,此时后台线程会执行Worker函数的代码。所以在这个程序中有两个线程,一个是运行Main函数的主线程,一个是运行Worker线程的后台线程。 ``` > 线程的容器-线程池 - 线程池是指用来存放应用程序中要使用的线程集合,可以将它理解为一个存放线程的地方,这种集中存放的方式有利于对线程进行管理。 - 通过线程池来实现多线程 - 要使用线程池的线程,需要调用静态方法ThreadPool.QueueUserWorkItem,以指定线程要调用的方法,该静态方法有两个重载版本: public static bool QueueUserWorkItem(WaitCallback callBack); public static bool QueueUserWorkItem(WaitCallback callback,Object state) 这两个方法用于向线程池队列添加一个工作先以及一个可选的状态数据。然后,这两个方法就会立即返回。下面通过实例来演示如何使用线程池来实现多线程编程。 ``` using System; using System.Threading; namespace 多线程2 { class Program { static void Main(string[] args) { Console.WriteLine($"主线程ID={Thread.CurrentThread.ManagedThreadId}"); ThreadPool.QueueUserWorkItem(CallBackWorkItem); ThreadPool.QueueUserWorkItem(CallBackWorkItem,"work"); Thread.Sleep(3000); Console.WriteLine("主线程退出"); Console.ReadKey(); } private static void CallBackWorkItem(object state) { Console.WriteLine("线程池线程开始执行"); if (state != null) { Console.WriteLine($"线程池线程ID={Thread.CurrentThread.ManagedThreadId},传入的参数为{state.ToString()}"); } else { Console.WriteLine($"线程池线程ID={Thread.CurrentThread.ManagedThreadId}"); } } } } ``` - 协作式取消线程池线程 - .NET Framework提供了取消操作的模式,这个模式是协作式的。为了取消一个操作,必须创建一个System.Threading.CancellationTokenSource对象。 ``` using System; using System.Threading; namespace 多线程3 { internal class Program { private static void Main(string[] args) { Console.WriteLine("主线程运行"); var cts = new CancellationTokenSource(); ThreadPool.QueueUserWorkItem(Callback, cts.Token); Console.WriteLine("按下回车键来取消操作"); Console.Read(); cts.Cancel(); Console.ReadKey(); } private static void Callback(object state) { var token = (CancellationToken) state; Console.WriteLine("开始计数"); Count(token, 1000); } private static void Count(CancellationToken token, int count) { for (var i = 0; i < count; i++) { if (token.IsCancellationRequested) { Console.WriteLine("计数取消"); return; } Console.WriteLine($"计数为:{i}"); Thread.Sleep(300); } Console.WriteLine("计数完成"); } ``` > 线程同步 - 线程同步计数是指多线程程序中,为了保证后者线程,只有等待前者线程完成之后才能继续执行。这就好比生活中排队买票,在前面的人没买到票之前,后面的人必须等待。 - 多线程程序中存在的隐患:多线程可能同时去访问一个共享资源,这将损坏资源中所保存的数据。这种情况下,智能采用线程同步技术 - 监视器对象能够保线程拥有对共享资源的互斥访问权。 > 线程同步技术存在问题 - 使用比较繁琐。要用额外的代码把多个线程同时访问的数据包围起来,还并不能遗漏。 - 使用线程同步会影响程序性能。因为获取和释放同步锁是需要时间的;并且决定那个线程先获得锁的时候,CPU也要进行协调。这些额外的工作会对性能造成影响。 - 线程同步每次只允许一个线程访问资源,这会导致线程堵塞。继而系统会创建更多的线程,CPU也就要负担更繁重的调度工作。这个过程会对性能造成影响。