多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。 在学习多线程前,我们需要了解下了解下进程和线程。
进程
进程、线程等并不是C#或者.Net有的机制,是操作系统提供的,程序代码中的线程是向操作系统申请的。C#等高级语言只是将它们封装、引入自己的体系中,并提供一系列的类、方法、编程模型等,方便开发者使用。
可以打开Windows任务管理器,查看当前运行的进程。
进程(Process)是操作系统中的一个基本概念, 他包含着一个运行程序所需要的资源。当启动程序时,系统便会在内存中创建一个新的进程,进程是构成运行程序资源的集合。
进程之间是相互独立的,每个进程是由私有的。一个进程无法访问另一个进程的数据,一个进程运行失败也不会影响其他进程的运行,操作系统就是利用进程将工作划分为多个独立的区域。
进程可以理解为是应用程序的执行实例,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。
线程
在进程的内部,有称为线程的内核对象,线程是进程内部的一个执行单元。进程的所有操作都是由线程去执行。
一个进程可以包含若干个线程,但是只有一个主执行线程,它代表的是真正的执行程序, 在进程入口执行的第一个线程被视为这个进程的主执行线程。
在.NET应用程序中,都是以 Main() 方法作为入口的,当调用此方法时,系统就会自动创建一个主线程。主执行线程终止了,进程也就随之终止。
每个线程都有维护异常的处理程序、调度优先级和线程上下文。线程上下文,保存了当前线程的执行环境和计算资源,当前执行的线程在其时间片结束时被挂起,而另一个线程继续运行。当系统从一个线程切换到另一个线程时,它将保存被抢先的线程的线程上下文,并重新加载线程队列中下一个线程的已保存线程上下文。
多线程
上述关于进程和线程的说明,可以简单概况为以下几点:
- 进程和线程是底层操作系统的机制;
- 进程是一个运行程序资源的集合;
- 线程是进程内的基本执行单元,进程的任何操作是由线程去执行;
- 线程是依托于进程存在的,一个进程可以包含多个线程;
1、多线程概念
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
2、多线程执行的原理
- CPU运行速度太快,硬件处理速度跟不上,所以操作系统进行分时间片管理。这样,从宏观角度来说是多线程并发的,因为CPU速度太快,察觉不到,看起来是同一时刻执行了不同的操作。但是从微观角度来讲,同一时刻只能有一个线程在处理。
- 目前电脑都是多核多CPU的,一个CPU在同一时刻只能运行一个线程,但是多个CPU在同一时刻就可以运行多个线程。
3、多线程优点/缺点
优点:
- 可以同时完成多个任务;
- 可以使程序的响应速度更快;
- 可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待该线程(卡死),这样就大大提高了程序的效率,(牺牲空间计算资源, 来换取时间)。
缺点:
- 线程也是程序(包含了当前操作的上下文环境和计算资源), 所以线程运行需要占用计算机内存空间,执行线程越多,占用内存越多(占内存多)
- 多线程需要协调和管理,CPU运行时会进行跟踪线程,消耗CPU资源(占CPU多)
- 线程之间对共享资源的访问会相互影响,必须解决争用共享资源的问题(共享资源访问安全问题)
C#中的多线程
在C#中,Thread/ThreadPool/Task 都是C#语言在操作计算机的线程时封装提供的帮助类库。
**1、Thread **
Thread是.Net最早的多线程处理方式,它出现在.Net1.0时代,虽然现在已逐渐被微软所抛弃,微软强烈推荐使用Task,但从多线程完整性的角度上来说,我们有必要了解下早期多线程的是怎么处理的,以便体会.Net体系中多线程处理方式的进化。
在.Net1.0时代,线程是使用Thread类处理的,该类在System.Threading命名空间中。
使用Thread类创建线程时,只需要提供线程入口,线程入口告诉程序让这个线程做什么。通过实例化一个Thread类的对象就可以创建一个线程。Thread类接收一个ThreadStart委托或ParameterizedThreadStart委托的构造函数,该委托包装了调用Start方法时由新线程调用的方法,示例代码如下:
ThreadStart threadStart = new ThreadStart(() =>
{
this.DoSomething("张三");
});
Thread thread = new Thread(threadStart);
thread.Start();
ThreadStart 是线程的入口,可以理解为一个函数指针,指向线程将要运行的函数。
2、ThreadPool
2.1 对象池
在系统设计中,我们经常会使用到“池”的概念。例如:数据库连接池。“池”可以节省对象重复创建和初始化所耗费的时间。对那些被系统频繁请求和使用的对象,使用此机制可以提高系统运行性能。
“池”是一种“以空间换时间”的做法,我们在内存中保存一系列整装待命的对象,供人随时差遣。与系统效率相比,这些对象所占用的内存空间太微不足道了。
通过对“对象池”的一个大体认识能帮我们更快理解线程池。
2.2 线程池 ThreadPool
.NET2.0时代,出现了一个线程池ThreadPool,是一种池化思想,如果需要使用线程,就可以直接到线程池中去获取直接使用,如果使用完毕,在自动的回放到线程池去;为应用程序提供一个由系统管理的辅助线程池,从而使您可以集中精力于应用程序任务而不是线程管理。
每个进程都有一个线程池,一个Process中只能有一个实例,它在各个应用程序域(AppDomain)是共享的。