C# 线程知识汇总
一、基本概念
进程(process)是windows系统中你的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或者多个线程。线程是操作系统分配处理器时间的基本单元吗,在进程中可以有多个线程同时执行代码。进程之间是相互独立的,一个进程无法访问另外一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行。进程可以理解为一个程序的边界。是程序的一个实例,是程序的一次动态执行过程。
线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。
多线程的优点:可以同时完成多个任务;可以使程序的响应速度更快;可以让占用大量处理时间的任务或当前没有进行处理的任务定期将处理时间让给别的任务;可以随时停止任务;可以设置每个任务的优先级以优化程序性能。
然而,多线程虽然有很多优点,但是也必须认识到多线程可能存在影响系统性能的不利方面,才能正确使用线程。不利方面主要有如下几点:
(1)线程也是程序,所以线程需要占用内存,线程越多,占用内存也越多。
(2)多线程需要协调和管理,所以需要占用CPU时间以便跟踪线程。
(3)线程之间对共享资源的访问会相互影响,必须解决争用共享资源的问题。
(4)线程太多会导致控制太复杂,最终可能造成很多程序缺陷。
二、实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ThreadDemo { public class ThreadTest { public void Run() { Console.WriteLine( "这是无参数,无返回值的方法" ); } /// <summary> /// 方法里面的参数类型必须是Object类型 /// </summary> /// <param name="paramer"></param> public void Run( object paramer) { Console.WriteLine( "这是有参数无返回值的方法,参数是:{0}" ,paramer.ToString()); } public int Run( int paramer) { Console.WriteLine( "这是有参数有返回值的方法,参数是:{0}" ,paramer); return paramer; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadDemo { class Program { public delegate int FunTest( int paramer); static void Main( string [] args) { testClass.run(); ThreadTest test = new ThreadTest(); //调用无参数无返回值 Thread t1 = new Thread( new ThreadStart(test.Run)); t1.Name = "no paramer no result" ; t1.Start(); //调用有参数没有返回值 Thread t2 = new Thread( new ParameterizedThreadStart(test.Run)); t2.Name = "paramert an no result" ; t2.Start(1); //调用有参数有返回值 FunTest ft = new FunTest(test.Run); IAsyncResult result = ft.BeginInvoke(1, null , null ); Console.ReadKey(); } } } |
三、锁
我们希望在某一个时刻,只有一个线程能执行某一段代码块,这个时候就需要用到锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Lock { public class ThreadTest { /* Lock 和Monitor异同: 1.他们都是指在对象上获取排他锁,用于同步代码区 2.Lock关键字是Monitor的一种替换用法,Lock在IL代码中会被翻译承Monitor 所以lock能做的,Monitor肯定能做,Monitor能做的,lock不一定能做 */ private static object obj = new object (); public static int [] arr = { 1, 2, 3, 4 }; public void LockRun() { //加锁之后会按照正常顺序输出 //不加锁顺序被打乱 lock (obj) { run(); } } public void MonitorRun() { //lock是monitor的语法糖,最后都会被翻译承下面这个 try { Monitor.Enter(obj); run(); } catch (Exception) { throw ; } finally { Monitor.Exit(obj); } } public void run() { for ( int i = 0; i < arr.Length; i++) { Console.WriteLine( "当前线程名:" + Thread.CurrentThread.Name + ". value:" + arr[i]); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Lock { class Program { static void Main( string [] args) { ThreadTest test1 = new ThreadTest(); Thread t1 = new Thread( new ThreadStart(test1.LockRun)); t1.Name = "test thread 1" ; t1.Start(); ThreadTest test2 = new ThreadTest(); Thread t2 = new Thread( new ThreadStart(test2.LockRun)); t2.Name = "test thread 2" ; t2.Start(); ThreadTest test3 = new ThreadTest(); Thread t3 = new Thread( new ThreadStart(test3.MonitorRun)); t3.Name = "test thread 3" ; t3.Start(); Console.ReadKey(); } } } |
四、Monitor
Monitor 是静态类,不能实例化
Monitor背后管理着2个队列:一个就绪队列(ready 队列),还有一个等待队列(waiting队列)
ready队列中保存的就是准备获取锁的线程,就是说,如果某个线程执行了waiting,那么ready队列中的第一个线程就会获得锁,开始运行,同时该线程进入waiting队列的队尾。
ready队列中保存的是准备获取锁的线程。就是说,如果某个线程(记作线程A)执行了Monitor.Wait(),那么ready队列中的第一个线程就会获得锁,开始运行(如果ready队列中没有线程, 那么就没有这个效果);同时线程A自动进入waiting队列中的队尾了。
waiting队列中保存的是正在等待锁定对象状态变化的通知的线程。就是说,如果某个线程执行了Monitor.Pulse(),那么waiting队列中队头的线程就进入ready队列中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp4 { class Program { static readonly object _locker = new object (); static bool go; static void Main( string [] args) { new Thread(Work).Start(); Console.ReadLine(); lock (_locker) { go = true ; Monitor.Pulse(_locker); } Thread.Sleep(1000); Console.ReadKey(); } static void Work() { lock (_locker) { while (!go) { // 把拥有当前锁的线程放入队列中 Monitor.Wait(_locker); } } Console.WriteLine( "被唤醒了。" ); } } } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步