# [C# 线程处理系列]专题一:线程基础
**引言:**
最近一段时间都在研究关于.Net线程的内容,觉得线程是每个程序员都应该掌握的,所以写下这个线程的系列希望能给大家学习过程中一些帮助,同时也是自己对线程的巩固,当中如果有什么错漏还请大家指出,这样我们可以互相得到进步。
**目录**:
一、线程的介绍
二、线程调度和优先级
三、前台线程和后台线程
四、简单线程的使用
**一、线程的介绍**
在介绍线程之前, 很有必要知道什么是进程,以及与线程的关系。
**进程**(Process)是应用程序的实例要使用的**资源**的一个集合(从可以简化理解:进程就是一种**资源**,是应用程序所用的资源)。每个应用程序都在各自的进程中运行来确保应用程序不受其他应用程序的影响,如果一个应用程序失败了, 只会影响自己的进程,其他进程中的应用程序可以继续运行。进程是操作系统为我们提供的一种保护应用程序的一种机制。
**线程**是进程中基本执行单元, 一个进程中可以包含多个线程,在进程入口执行的第一个线程是一个进程的主线程,在.Net应用程序中,都是以Main()方法作为程序的入口的, 所以在程序运行过程中调用这个方法时,系统就会自动创建一个主线程。(他们之间的关系简单说:线程是进程的执行单元,进程是线程的一个容器了)。
**二、线程调度和优先级**
Windows之所以被称为抢占式多线程操作系统,是因为线程可以在任意时间被抢占,并调度另一个线程。每个线程都分配了从0~31的一个优先级。系统首先把高优先级的线程分配给CPU执行。Windows 支持7个相对线程优先级:Idle,Lowest,Below Normal,Normal,Above Normal,Highest和Time-Critical,Normal是默认的线程优先级,然而在程序中可以通过设置Thread的Priority属性来改变线程的优先级,它的类型为ThreadPriority枚举类型,包含枚举有:Lowest,BelowNormal,Normal,AboveNormal和Highest,CLR为自己保留了 Idle和Time-Critical优先级。具体每个枚举值含义如下表:
| 成员名称 | 说明 |
| --- | --- |
| Lowest | Thread can be scheduled after threads with any other priority." data-guid="3e53547e0e9a509aff4a76382e494083">可以将 Thread何其他优先级的线程之后。 |
| BelowNormal | Thread can be scheduled after threads with Normal priority and before those with Lowest priority." data-guid="f979b4a5dfbb5a35942312092b2cd47a">可以将 Thread Normal 优先级的线程之后,在具有 Lowest 优先级的线程之前。 |
| Normal | Thread can be scheduled after threads with AboveNormal priority and before those with BelowNormal priority." data-guid="8b94d9644aacd17640f4971fc6e79dc7">可以将 Thread AboveNormal 优先级的线程之后,在具有 BelowNormal 优先级的线程之前。 Normal priority by default." data-guid="6fa67ded483ad3d242ea365f0ac225c4">默认情况下,线程具有 Normal 优先级。 |
| AboveNormal | Thread can be scheduled after threads with Highest priority and before those with Normal priority." data-guid="d067edb8ea0b5bfd51a2591334b86fd7">可以将 Thread Highest 优先级的线程之后,在具有 Normal 优先级的线程之前。 |
| Highest | Thread can be scheduled before threads with any other priority." data-guid="fc7a0e931c772d08fc98b6de2c82ab3c">可以将 Thread 其他优先级的线程之前。 |
**三、前台线程和后台线程**
在.net中线程分为前台线程和后台线程,在一个进程中,当所有前台线程停止运行时,CLR会强制结束仍在运行的任何后台线程,这些后台线程直接被终止,不会抛出异常。
所以我们应该在前台线程中执行我们确实要完成的事情,另外, 应该把非关键的任务使用后台线程,我们用Thread创建的是线程为前台线程。让我们通过下面的一段代码来看看前台线程和后台线成的区别:
```
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
// 创建一个新线程(默认为前台线程)
Thread backthread = new Thread(Worker);
// 使线程成为一个后台线程
backthread.IsBackground = true;
// 通过Start方法启动线程
backthread.Start();
// 如果backthread是前台线程,则应用程序大约5秒后才终止
// 如果backthread是后台线程,则应用程序立即终止
Console.WriteLine("Return from Main Thread");
}
private static void Worker()
{
// 模拟做10秒
Thread.Sleep(5000);
// 下面语句,只有由一个前台线程执行时,才会显示出来
Console.WriteLine("Return from Worker Thread");
}
}
```
运行上面代码可以发现:控制台中显示字符串: Return form Main Thread 后就退出了, 字符串 Return from Worker Thread字符串根本就没有显示,这是因为此时的backthread线程为后台线程,当主线程(执行Main方法的线程,主线程当然也是前台线程了)结束运行后,CLR会强制终止后台线程的运行,整个进程就被销毁了,并不会等待后台线程运行完后才销毁。如果把 backthread.IsBackground = true; 注释掉后, 就可以看到控制台过5秒后就输出 Return from Worker Thread。再在Worker方法最后加一句 代码:Console.Read(); 就可以看到这样的结果了:
![](https://box.kancloud.cn/2016-01-23_56a2eb31a54bd.png)
**注意**:有些人可能会问我不想把 backthread.IsBackground = true;注释掉, 又想把Worker()方法中的字符串输出在控制台上怎么做呢? 其实是有解决的办法的, 我们可以调用thread.Join()方法来实现,Join()方法能保证主线程(前台线程)在异步线程thread(后台线程)运行结束后才会运行。
实现代码如下:
```
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
// 创建一个新线程(默认为前台线程)
Thread backthread = new Thread(Worker);
// 使线程成为一个后台线程
backthread.IsBackground = true;
// 通过Start方法启动线程
backthread.Start();
backthread.Join();
// 模拟主线程的输出
Thread.Sleep(2000);
Console.WriteLine("Return from Main Thread");
Console.Read();
}
private static void Worker()
{
// 模拟做3秒
Thread.Sleep(3000);
// 下面语句,只有由一个前台线程执行时,才会显示出来
Console.WriteLine("Return from Worker Thread");
}
}
```
运行结果(调用Join方法后后台线程会阻塞主线程所以主线程会后输出):
![](https://box.kancloud.cn/2016-01-23_56a2eb31b4881.jpg)
**四、简单线程的使用**
其实在上面介绍前台线程和后台线程的时候已经通过ThreadStart委托创建了一个线程了,此时已经实现了一个多线程的一个过程,为此系列中将多线程也是做一个铺垫吧。下面通过**ParameterizedThreadStart**委托的方式来实现多线程。
以**ParameterizedThreadStart**委托的方式来实现多线程:
```
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
// 创建一个新线程(默认为前台线程)
Thread backthread = new Thread(new ParameterizedThreadStart(Worker));
// 通过Start方法启动线程
backthread.Start("123");
// 如果backthread是前台线程,则应用程序大约5秒后才终止
// 如果backthread是后台线程,则应用程序立即终止
Console.WriteLine("Return from Main Thread");
}
private static void Worker(object data)
{
// 模拟做5秒
Thread.Sleep(5000);
// 下面语句,只有由一个前台线程执行时,才会显示出来
Console.WriteLine(data + " Return from Worker Thread");
Console.Read();
}
}
```
注意:此时Worker方法传入了一个参数,并且Start方法也传递了一个字符传参数。 对比与之前创建Thread的不同,
运行结果为:
![](https://box.kancloud.cn/2016-01-23_56a2eb31c5f66.png)
写到这里, 本系列的第一篇差不多讲完了,在后续的文章将会介绍Thread方法的使用以及通过一些例子来展示他们的不同之处(像Abort()方法Interrupt方法等)对于线程的一些高级使用(如线程池,并行编程和PLINQ、线程同步和计时器)都会在后续中讲到。希望本系列可以给初学线程的人有所帮助。
- C# 基础知识系列
- C# 基础知识系列 专题一:深入解析委托——C#中为什么要引入委托
- C# 基础知识系列 专题二:委托的本质论
- C# 基础知识系列 专题三:如何用委托包装多个方法——委托链
- C# 基础知识系列 专题四:事件揭秘
- C# 基础知识系列 专题五:当点击按钮时触发Click事件背后发生的事情
- C# 基础知识系列 专题六:泛型基础篇——为什么引入泛型
- C# 基础知识系列 专题七: 泛型深入理解(一)
- C# 基础知识系列 专题八: 深入理解泛型(二)
- C# 基础知识系列 专题九: 深入理解泛型可变性
- C#基础知识系列 专题十:全面解析可空类型
- C# 基础知识系列 专题十一:匿名方法解析
- C#基础知识系列 专题十二:迭代器
- C#基础知识 专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
- C# 基础知识系列 专题十四:深入理解Lambda表达式
- C# 基础知识系列 专题十五:全面解析扩展方法
- C# 基础知识系列 专题十六:Linq介绍
- C#基础知识系列 专题十七:深入理解动态类型
- 你必须知道的异步编程 C# 5.0 新特性——Async和Await使异步编程更简单
- 全面解析C#中参数传递
- C#基础知识系列 全面解析C#中静态与非静态
- C# 基础知识系列 C#中易混淆的知识点
- C#进阶系列
- C#进阶系列 专题一:深入解析深拷贝和浅拷贝
- C#进阶系列 专题二:你知道Dictionary查找速度为什么快吗?
- C# 开发技巧系列
- C# 开发技巧系列 使用C#操作Word和Excel程序
- C# 开发技巧系列 使用C#操作幻灯片
- C# 开发技巧系列 如何动态设置屏幕分辨率
- C# 开发技巧系列 C#如何实现图片查看器
- C# 开发技巧 如何防止程序多次运行
- C# 开发技巧 实现属于自己的截图工具
- C# 开发技巧 如何使不符合要求的元素等于离它最近的一个元素
- C# 线程处理系列
- C# 线程处理系列 专题一:线程基础
- C# 线程处理系列 专题二:线程池中的工作者线程
- C# 线程处理系列 专题三:线程池中的I/O线程
- C# 线程处理系列 专题四:线程同步
- C# 线程处理系列 专题五:线程同步——事件构造
- C# 线程处理系列 专题六:线程同步——信号量和互斥体
- C# 多线程处理系列专题七——对多线程的补充
- C#网络编程系列
- C# 网络编程系列 专题一:网络协议简介
- C# 网络编程系列 专题二:HTTP协议详解
- C# 网络编程系列 专题三:自定义Web服务器
- C# 网络编程系列 专题四:自定义Web浏览器
- C# 网络编程系列 专题五:TCP编程
- C# 网络编程系列 专题六:UDP编程
- C# 网络编程系列 专题七:UDP编程补充——UDP广播程序的实现
- C# 网络编程系列 专题八:P2P编程
- C# 网络编程系列 专题九:实现类似QQ的即时通信程序
- C# 网络编程系列 专题十:实现简单的邮件收发器
- C# 网络编程系列 专题十一:实现一个基于FTP协议的程序——文件上传下载器
- C# 网络编程系列 专题十二:实现一个简单的FTP服务器
- C# 互操作性入门系列
- C# 互操作性入门系列(一):C#中互操作性介绍
- C# 互操作性入门系列(二):使用平台调用调用Win32 函数
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(四):在C# 中调用COM组件
- CLR
- 谈谈: String 和StringBuilder区别和选择
- 谈谈:程序集加载和反射
- 利用反射获得委托和事件以及创建委托实例和添加事件处理程序
- 谈谈:.Net中的序列化和反序列化
- C#设计模式
- UML类图符号 各种关系说明以及举例
- C#设计模式(1)——单例模式
- C#设计模式(2)——简单工厂模式
- C#设计模式(3)——工厂方法模式
- C#设计模式(4)——抽象工厂模式
- C#设计模式(5)——建造者模式(Builder Pattern)
- C#设计模式(6)——原型模式(Prototype Pattern)
- C#设计模式(7)——适配器模式(Adapter Pattern)
- C#设计模式(8)——桥接模式(Bridge Pattern)
- C#设计模式(9)——装饰者模式(Decorator Pattern)
- C#设计模式(10)——组合模式(Composite Pattern)
- C#设计模式(11)——外观模式(Facade Pattern)
- C#设计模式(12)——享元模式(Flyweight Pattern)
- C#设计模式(13)——代理模式(Proxy Pattern)
- C#设计模式(14)——模板方法模式(Template Method)
- C#设计模式(15)——命令模式(Command Pattern)
- C#设计模式(16)——迭代器模式(Iterator Pattern)
- C#设计模式(17)——观察者模式(Observer Pattern)
- C#设计模式(18)——中介者模式(Mediator Pattern)
- C#设计模式(19)——状态者模式(State Pattern)
- C#设计模式(20)——策略者模式(Stragety Pattern)
- C#设计模式(21)——责任链模式
- C#设计模式(22)——访问者模式(Vistor Pattern)
- C#设计模式(23)——备忘录模式(Memento Pattern)
- C#设计模式总结
- WPF快速入门系列
- WPF快速入门系列(1)——WPF布局概览
- WPF快速入门系列(2)——深入解析依赖属性
- WPF快速入门系列(3)——深入解析WPF事件机制
- WPF快速入门系列(4)——深入解析WPF绑定
- WPF快速入门系列(5)——深入解析WPF命令
- WPF快速入门系列(6)——WPF资源和样式
- WPF快速入门系列(7)——深入解析WPF模板
- WPF快速入门系列(8)——MVVM快速入门
- WPF快速入门系列(9)——WPF任务管理工具实现
- ASP.NET 开发
- ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上
- ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理
- ASP.NET中实现回调
- 跟我一起学WCF
- 跟我一起学WCF(1)——MSMQ消息队列
- 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(4)——第一个WCF程序
- 跟我一起学WCF(5)——深入解析服务契约 上篇
- 跟我一起学WCF(6)——深入解析服务契约 下篇
- 跟我一起学WCF(7)——WCF数据契约与序列化详解
- 跟我一起学WCF(8)——WCF中Session、实例管理详解
- 跟我一起学WCF(9)——WCF回调操作的实现
- 跟我一起学WCF(10)——WCF中事务处理
- 跟我一起学WCF(11)——WCF中队列服务详解
- 跟我一起学WCF(12)——WCF中Rest服务入门
- 跟我一起学WCF(13)——WCF系列总结
- .NET领域驱动设计实战系列
- .NET领域驱动设计实战系列 专题一:前期准备之EF CodeFirst
- .NET领域驱动设计实战系列 专题二:结合领域驱动设计的面向服务架构来搭建网上书店
- .NET领域驱动设计实战系列 专题三:前期准备之规约模式(Specification Pattern)
- .NET领域驱动设计实战系列 专题四:前期准备之工作单元模式(Unit Of Work)
- .NET领域驱动设计实战系列 专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
- .NET领域驱动设计实战系列 专题六:DDD实践案例:网上书店订单功能的实现
- .NET领域驱动设计实战系列 专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
- .NET领域驱动设计实战系列 专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现
- .NET领域驱动设计实战系列 专题九:DDD案例:网上书店AOP和站点地图的实现
- .NET领域驱动设计实战系列 专题十:DDD扩展内容:全面剖析CQRS模式实现
- .NET领域驱动设计实战系列 专题十一:.NET 领域驱动设计实战系列总结