线程与内存在前面的讨论中我们知道了线程和CPU的关系,也就是把CPU的PC寄存器指向线程的入口函数,这样线程就可以运行起来了,这就是为什么我们创建线程时必须指定一个入口函数的原因 。无论使用任何编程语言,创建一个线程大体相同:
// 设置线程入口函数DoSomethingthread = CreateThread(DoSomething);// 让线程运行起来thread.Run();那么线程和内存又有什么关联呢?
我们知道函数在被执行的时产生的数据包括函数参数、局部变量、返回地址等信息,这些信息是保存在栈中的,线程这个概念还没有出现时进程中只有一个执行流,因此只有一个栈,这个栈的栈底就是进程的入口函数,也就是main函数,假设main函数调用了funA,funcA又调用了funcB,如图所示:

文章插图
图片
那么有了线程以后了呢?
有了线程以后一个进程中就存在多个执行入口,即同时存在多个执行流,那么只有一个执行流的进程需要一个栈来保存运行时信息,那么很显然有多个执行流时就需要有多个栈来保存各个执行流的信息,也就是说操作系统要为每个线程在进程的地址空间中分配一个栈,即每个线程都有独属于自己的栈,能意识到这一点是极其关键的 。

文章插图
图片
同时我们也可以看到,创建线程是要消耗进程内存空间的,这一点也值得注意 。
线程的使用现在有了线程的概念,那么接下来作为程序员我们该如何使用线程呢?
从生命周期的角度讲,线程要处理的任务有两类:长任务和短任务 。
1,长任务,long-lived tasks顾名思义,就是任务存活的时间很长,比如以我们常用的word为例,我们在word中编辑的文字需要保存在磁盘上,往磁盘上写数据就是一个任务,那么这时一个比较好的方法就是专门创建一个写磁盘的线程,该写线程的生命周期和word进程是一样的,只要打开word就要创建出该写线程,当用户关闭word时该线程才会被销毁,这就是长任务 。
这种场景非常适合创建专用的线程来处理某些特定任务,这种情况比较简单 。
有长任务,相应的就有短任务 。
2,短任务,short-lived tasks这个概念也很简单,那就是任务的处理时间很短,比如一次网络请求、一次数据库查询等,这种任务可以在短时间内快速处理完成 。因此短任务多见于各种Server,像web server、database server、file server、mail server等,这也是互联网行业的同学最常见的场景,这种场景是我们要重点讨论的 。
这种场景有两个特点:一个是任务处理所需时间短;另一个是任务数量巨大 。
【高并发中的线程与线程池】如果让你来处理这种类型的任务该怎么办呢?
你可能会想,这很简单啊,当server接收到一个请求后就创建一个线程来处理任务,处理完成后销毁该线程即可,So easy 。
这种方法通常被称为thread-per-request,也就是说来一个请求就创建一个线程:

文章插图
图片
如果是长任务,那么这种方法可以工作的很好,但是对于大量的短任务这种方法虽然实现简单但是有这样几个缺点:
1. 从前几节我们能看到,线程是操作系统中的概念(这里不讨论用户态线程实现、协程之类),因此创建线程天然需要借助操作系统来完成,操作系统创建和销毁线程是需要消耗时间的
2. 每个线程需要有自己独立的栈,因此当创建大量线程时会消耗过多的内存等系统资源
这就好比你是一个工厂老板(想想都很开心有没有),手里有很多订单,每来一批订单就要招一批工人,生产的产品非常简单,工人们很快就能处理完,处理完这批订单后就把这些千辛万苦招过来的工人辞退掉,当有新的订单时你再千辛万苦的招一遍工人,干活儿5分钟招人10小时,如果你不是励志要让企业倒闭的话大概是不会这么做到的,因此一个更好的策略就是招一批人后就地养着,有订单时处理订单,没有订单时大家可以闲呆着 。
这就是线程池的由来 。
从多线程到线程池线程池的概念是非常简单的,无非就是创建一批线程,之后就不再释放了,有任务就提交给这些线程处理,因此无需频繁的创建、销毁线程,同时由于线程池中的线程个数通常是固定的,也不会消耗过多的内存,因此这里的思想就是复用、可控 。
推荐阅读
- cad如何输出高清图片?这些方法了解一下
- 最高年薪30万! 合肥一上市公司开启招聘模式!
- 聂远夫妇好宠女儿,9岁天天玩偶堆积如山价值不菲,超高颜值吸睛
- 高铁小刀规定几厘米 坐动车可以带小刀吗
- 99版1元纸币,很多人都认为收藏价值很高,实际上是真的吗
- 应届生应聘助理岗位,期待薪资过高被HR嘲讽,评论区看法很一致
- 玫瑰如何扦插成活率高一些 玫瑰如何扦插成活率高
- 女篮第一女神谈恋爱,身材傲人颜值高,网友:比李梦漂亮
- 粽子的热量 粽子的热量高吗减肥能吃吗
- 佟丽娅40岁生日,7岁儿子举气球为她庆祝,朵朵身高猛长体型壮实
