一、概述
线程被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及复杂且耗时的操作,那么设置不同的线程执行路径往往事半功倍,让每个线程执行特定的工作任务。
线程是一个轻量级进程。一个使用线程的常见实例是操作系统中并行编程的实现。使用线程节省了CPU周期的浪费,同时提高了应用程序的效率。本文将讲详细讲解Thread线程的用法。
Thread类位于System.Threading命名空间下,System.Threading命名空间提供一些可以进行多线程编程的类和接口。除同步线程活动和访问数据的类(Mutex,Monitor,Interlocked和AutoResetEvent等)外,该命名空间还包含一个ThreadPool类(它允许用户使用系统提供的线程池)和一个Timer类(它在线程池的线程上执行回调方法)。
二、线程的创建和使用
Thread
创建一个线程非常简单,只需要将其声明并为其提供线程起始点处的委托即可。创建新的线程时,需要使用Thread
类,该类具有接受一个ThreadStart
委托或ParameterizedThreadStart
委托的构造函数。
2.1 ThreadStart用于无返回值,无参数的方法
public static void Main(string[] args) { ThreadStart threadStart = new ThreadStart(MyThread); Console.WriteLine("创建一个线程"); Thread thread= new Thread(threadStart); thread.Start(); } private static void MyThread() { Console.WriteLine("This is Thread Method."); Thread.Sleep(2000); Console.WriteLine("Thread is over....."); }
运行结果:
创建一个线程 This is Thread Method. Thread is over.....
2.2 ParameterizedThreadStart:用于带参数的方法
public static void Main(string[] args) { ParameterizedThreadStart threadStart = new ParameterizedThreadStart(MyThread); Console.WriteLine("创建一个线程"); Thread thread= new Thread(threadStart); thread.Start(100); } private static void MyThread(object n) { Console.WriteLine("This is Thread Method."); Thread.Sleep(2000); Console.WriteLine("Thread is over....."); for (int i = 0; i <= (int)n; i += 1) { Console.WriteLine(i); } }
三、线程的启动执行
线程创建后并不会理解执行,这是因为绝大多数系统不是一个实时的操作系统。在操作系统内部会实现特殊的算法进行线程之间的调度。在某个时刻它会决定当前运行哪个线程。看下面的示例:
static int id = 0; public static void Main(string[] args) { for (int i = 0; i < 10; i++) { id++; Thread thread = new Thread(() => { Console.WriteLine(string.Format("{0}:{1}", Thread.CurrentThread.Name, id)); }); thread.Name = string.Format("Thread{0}", i); thread.IsBackground = true; thread.Start(); } }
执行结果:
Thread1:3 Thread0:3 Thread2:4 Thread3:5 Thread4:6 Thread5:7 Thread6:8 Thread7:9 Thread8:10 Thread9:10
使用同步参数创建线程
static int id = 0; static void Main(string[] args) { for (int i = 0; i < 5; i++, id++) { NewMethod(i, id); } Console.ReadLine(); } private static void NewMethod(int i, int realTimeId) { Thread thread = new Thread(() => { Console.WriteLine(string.Format("{0}:{1}", Thread.CurrentThread.Name, realTimeId)); }); thread.Name = string.Format("Thread{0}", i); thread.IsBackground = true; thread.Start(); }
运行结果:
Thread0:0 Thread1:1 Thread2:2 Thread3:3 Thread4:4
四、线程的挂起和恢复
创建完一个线程并启动后,还可以挂起、恢复、休眠或终止线程。线程的挂起与恢复可以通过调用Thread
类的Suspend
方法和Resume
方法实现。
4.1 Suspend方法
该方法用来挂起线程,如果线程已挂起,则不起作用。调用Suspend
方法挂起线程时,.NET允许挂起的线程再执行几个指令,目的是为了到达.NET认为线程可以安全挂起的状态。
4.2 Resume方法
该方法用来继续已挂起的线程。通过Resume
方法来恢复被暂停的线程时,无论调用多少次Suspend
方法,调用Resume
方法均会使另一个线程脱落挂起状态。
通过Resume()
方法来恢复被暂停的线程时,无论调用了多少次Suspend()
方法,调用Resume()
方法均会使另一个线程脱离挂起状态,并导致该线程继续执行。
4.3 该方法已废弃及替代方法
Thread.Suspend()
和Thread.Resume()
这两个方法已过时,需要使用AutoResetEvent
、EventWaitHandle
代替。
五、中止线程(About)
线程被中止,就停止运行,是无法恢复的,因为操作系统会删除被中止线程所有数据。
static void Main(string[] args) { Thread myThread; myThread = new Thread(new ThreadStart(createThread)); myThread.Start(); myThread.Abort(); } private static void createThread() { for (long i = 0; i < 1000000000; i++) { Console.WriteLine($"workThread-->i={i}"); } }
线程中止和线程挂起是一样结果,中止工作线程后,线程将不会在被执行,如果强制运行程序,将出现运行时错误。
六、线程休眠(Sleep)
使线程休眠使用Sleep()
方法,此方法可以让线程休眠一定的时间,示例代码如下:
Thread workThread = new Thread(new ThreadStart(createThread)); workThread.Start(); Thread.Sleep(10000);//修改
Sleep()
会让线程暂停一段时间后接着运行,休眠线程不需要手动恢复,到指定的时间后会自动执行。
七、线程等待(Join())
如果后续的处理依赖于另一个已终止的线程,可调用Join()
方法,等待线程中止。
Thread workThread = new Thread(new ThreadStart(createThread)); workThread.Start(); workThread.Join();
工作线程调用了Jion()
方法,需待工作线程中止后,主线程才会被执行。Jion()
的其他重载方法可以指定等待的时间期限,超过了时间期限,程序会继续执行。
八、线程让步(Yield())
Yield 的中文翻译为 “屈服,让步”,这里意思是主动放弃当前线程的时间片,并让操作系统调度其它就绪态的线程使用一个时间片。但是如果调用 Yield,只是把当前线程放入到就绪队列中,而不是阻塞队列。如果没有找到其它就绪态的线程,则当前线程继续运行。Yield可以让低于当前优先级的线程得以运行,调用者可以通过返回值判断是否成功调度了其它线程。注意,Yield只能调度运行在当前CPU上的线程。
九、后台线程与前台线程
线程是后台线程或前台线程。后台线程与前台线程相同,只不过后台线程不会阻止进程终止。一旦属于进程的所有前台线程都终止,公共语言进行时将结束该进程。所有剩余的后台线程将停止,并且无法完成。
默认情况下,以下线程是前台线程:
- 主线程(或主应用程序线程)。
- 通过调用类构造函数创建
Thread
的所有线程都是前台线程。
默认情况下,以下线程在后台执行:
- 线程池线程,由运行时维护的工作线程池。可以使用类配置线程池并计划线程池线程
ThreadPool
上工作。 - 从非托管代码进入托管执行环境的所有线程。
十、Thread
线程属性
- CurrentThread:获取当前正在运行的线程。
- IsAlive :获取指示当前线程的执行状态的值。
- IsBackgroud:获取或设置一个值,该值指示某个线程是否为后台线程。
- IsThreadPoolThread:获取指示线是否属于托管线程池的值。
- Name:获取或设置线程的名称。
- Priority:获取或设置线程调度优先级的值。
- ThreadState:获取一个值,该值包含当前线程的状态。