C# 线程(一)——基础概念(线程与进程、前后台线程)
一、基础概念
1.1线程与进程
线程——是一个可执行路径,它可以独立于其他线程执行。
进程——每个线程都在操作系统的进程(Process)内执行,而操作系统则提供了程序运行的独立环境,它提供了一个应用程序的一个实例所需要使用资源的集合(执行一个.exe文件其实就是启动一个进程,也是启动一个应用程序实例),每个进程都至少有一个线程
比如进程启动时就会开辟一片独立的内存空间,进程内的所有线程共同享有这片内存,进程与进程之间的空间是独立的,彼此无法访问,这确保了进程与进程无法互相破坏。
1.2线程是怎样执行的
什么是线程上下文的切换?_房东的猫儿呀的博客-CSDN博客_什么是线程上下文
现有的计算机系统对线程(thread)的职责定义是对cpu虚拟化,简单理解就是每个cpu都是一个打工人。现在有很多份活等着他处理,比如xx音乐(音乐播放,音量调整,界面调整)、xx下载(下载、边看边播、界面操作)都等着他来做,现在的做法是将这些活儿细分到最小(细分成单独的线程),在某一时刻打工人只能做其中一件事。windows给每个线程分配一个时间片(执行时间),大约为30毫秒,cpu就在这个时间片时间内执行这个线程,时间片到期后切换到另外一个线程去执行。因为cpu执行速度非常快,30毫秒时间非常短,远低于人的感知,所以即使是单核计算机(单个打工人),在播放音乐的同时调整音量,在这两个线程内来回切换,人也无法感知。对于人来说是”同时“进行的。
线程上下文(thread context)——可以这么理解,cpu在线程之间来回切换执行时,需要做两件事
1、保存当前线程的执行进度
2、加载下一个要执行线程的原有进度
比如我在播放音乐的时候去调整音量,
1、我需要保存当前音乐的播放进度
2、我需要加载原来调整的音量刻度位置(不是每次都要从0加载)
这个进度保存是通过程序计数器实现的,每个线程都有独立的程序计数器,各条线程之间的计数器互补影响,存放在”线程私有“的内存里
注意:一个时间片结束,系统有可能会决定再次调用同一个线程,就不会执行上下文切换的动作,我们写代码的时候也要尽量避免上下文的切换,因为切换过程本身也是一种资源与时间消耗,即使非常小,在人的层面无法感知
问题一:线程锁的由来
对于进程内的共有资源,有多个线程竞争进程公共内存的公共资源时会需要加线程锁,防止多个线程同时操作同一个资源,造成类似数据库脏读的现象
问题二:windows系统为什么要采用线程来回切换执行的机制
试想一下如果我们采用一个线程执行完再执行下一个线程的机制(参照我们在主线程的行为,就是按顺序执行的),如果遇到大文件下载,如果我们是单核计算器,就不得不长时间等待十几分钟甚至几个小时等文件下载完才能做其他事,体验和效率都太不友好了
前后台线程
主线程——当一个程序(进程)启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread),主线程也是前台线程。
默认情况下,在代码中手动创建的线程是前台线程
只要有前台线程在运行,那么应用程序(进程)就会一直处于活动状态
例子:xx下载,比如我们打开xx下载,同时创建10个下载任务,我们删除其中一个程序还是照样进行,但是我们点击退出,就直接结束了应用。这个界面操作的线程就是前台线程(也是主线程),在它结束后,所有的后台线程(10下载任务)都会马上结束
线程优先级
本质上是调整分配各个线程的执行时间多少,并不是先执行,执行本身是无序的,后续有相关控制顺序执行的方法介绍。
线程池化
背景:当我们在进行多线程编程时,就需要创建线程,如果说程序并发很高的话,我们会创建大量的线程,而每个线程执行一个时间很短的任务就结束了,这样频繁创建线程,会极大的降低系统性能,增加服务器开销,因为创建线程和销毁线程都需要额外的消耗。线程池技术就是针对优化这一缺陷
池化技术介绍: 什么时池化技术呢?池化技术是一种编程技巧,当程序出现高并发时,能够明显的优化程序,降低系统频繁创建销毁连接等额外开销。我们经常接触到的池化技术有数据库连接池、线程池、对象池(依赖注入思想)等等。池化技术的特点是将一些高成本的资源维护在一个特定的池子(内存)中,规定其最小连接数、最大连接数、阻塞队列,溢出规则等配置,方便统一管理。一般情况下也会附带一些监控,强制回收等配套功能。
适用场景:
1、获取资源的频率很高,且使用资源总数有限(如果不固定好最大线程数量,一直不断开启新的线程、销毁,会耗费大量的cpu资源)
2、对处理时间的延迟比较高,响应速度比较快(线程的创建和销毁都需要时间,会拖慢响应的速度)