26

图解|打工人看腾讯这道多线程面试题

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzI1MzYzMTI2Ng%3D%3D&%3Bmid=2247485492&%3Bidx=1&%3Bsn=02755de75a49ead8563d7e087c2e7010
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1. 骚动的周五

小黑是大白前同事,现在俩人在不同的公司,但是都做后端开发工作。

虽然两个人都在北京,但是距离不算近,一个在望京,一个在中关村,算是北京几大IT聚集圈之二了。

两个人日常除了工作,业余活动并不多,当然头发也不多,宇宙中心五道口成了二人的集结地。

jQR32qv.png!mobile

眨了5次眼,又到周五了,仿佛空气都弥漫着明天放假的欢快气息,当然还有骚动的大白和小黑:

fEVvuu2.png!mobile

大白看着时间差不多了,检查完上线监控报警,没啥问题,背上电脑走出了写字楼。

中关村到五道口还是比较近的,扫上低碳环保的青桔单车,一路向北到北大东门转弯来到了五道口地区。

小黑也坐上13号线,人贴人差点挤成肉饼,美食召唤下他还是在8点准时到了老地方。

大白:黑哥,你啥时候面的腾讯?挂了?你咋不找我内推我们公司呀!

小黑:还没挂,等GM面呢,你们公司手撕红黑树,整不了啊。

大白:就你这样,这么喜欢穿红卫衣和黑裤子,不问你红黑树才怪。话说腾讯都问啥了?

小黑:腾讯的面试整体感觉还是不错的,面试很宽泛,从操作系统、网络到系统设计、常用组件都会问,并且不偏不怪。

大白:那确实不错,是本着去挖掘和探测候选人技术边界,有啥奈斯的问题吗?讲讲啊

小黑:有个问题算是我的盲区了,给了几个Linux系统函数,让我看哪些是线程安全的,哪些是可重入的,并解释下为啥。

大白:哦哦,这是考察对线程安全函数和可重入函数的理解。那你咋回答的?

小黑:卧槽,我说我不太会呀,然后就jump下一题了。要不你给我讲讲?我先干一个!

小黑说完,吨吨吨,一大杯啤酒下肚了,大白见状扶了扶好几年没换的眼镜,开始和小黑讨论什么是线程安全和可重入。

7ZFbYbV.png!mobile

2. 多线程和并发

在使用C++开发的服务端程序中多线程还是主流,一般来说会有个线程池来处理接收的请求,这样可以有效提供服务器的并发能力和CPU的利用率。

zeaU7n2.png!mobile

但是,多线程也是一把双刃剑。

单线程模式下,一切都是那么单调而稳定,所有的资源都是自己的,我的资源我做主。

多线程模式下,一个进程下装载了多个线程,每个线程除了部分资源是独享外,多个线程对大部分系统资源是共享的。

多个线程共享的进程资源:

  • 内存

  • 文件描述符

  • 地址空间

  • 全局数据

  • ...

每个线程独享的资源:

  • 线程寄存器

  • 线程栈

  • 线程ID、错误返回码、信号屏蔽码

  • ...

f6r2Ujm.png!mobile

敲黑板划重点:

1.进程是系统进行资源分配和调度的基本单位,线程是CPU调度和分派的基本单位;

2.进程是线程的载体,进程有独立地址空间,所有线程共享所在进程的地址空间;

3.进程是系统资源的大股东,而线程基本上不拥有系统资源,只占用少量在运行中必不可少的资源,比如程序计数器、一组寄存器和调用栈;

同一个进程中的多个线程有点像合租,大家共用大部分资源,自己独占一小部分资源,相互影响,然而但单进程单线程就是整租,自己独占所有资源,谁也不影响。

vAveeyu.png!mobile

掌握多线程中资源共享和相互影响的特点之后,再来看看线程安全和可重入就容易很多。

eu6ZVr3.png!mobile

3. 什么是线程安全

计算机中所谓的安全大多是指结果的正确且可预测性。

前面我们知道,多线程运行起来虽然可以提高并发能力,但是多个线程会共享很多资源,比如写全局数据,这种情况下就需要额外干预,否则将引发错乱的结果。

线程安全是在拥有共享数据的多条线程并行执行的进程中,可以正常且正确的执行,不会出现数据污染等意外情况,反之则称为线程不安全。

通俗一点讲,线程安全就怎么跑都不乱,线程不安全就是一跑就可能五花八门。

所以可能产生线程不安全根本原因在于:共享数据且共享数据可变。

这些共享数据包括全局变量、局部静态变量等,每个线程都可能对这个数据进行操作,并且操作结果会影响其他线程。

我们还经常提到另外一个术语:线程安全函数/线程安全类。

线程安全函数的一些特征:

  • 无任何共享的数据,都是局部数据;

  • 存在写共享数据,但是进行了加锁处理,可以实现多线程的同步调用;

  • 存在读但无写共享数据,无需加锁;

fqQvMnj.png!mobile

从图中可以看到:

  • 同一进程内有四个工作线程;

  • 公共函数A 只执行打印操作,无论何时何线程调用,结果都是确定且正确的,因此是线程安全函数;

  • 公共函数B 使用了全局变量Count,并对其进行递增1操作,但是没有进行加锁同步处理,因此结果是不确定的,为线程不安全函数;

  • 公共函数C 使用了全局变量Factor,并对其进行递增2操作,使用了互斥锁进行同步确保结果的正确,是线程安全函数;

在编写多线程程序时,如果涉及多个线程操作一个公共函数,如果该函数本身不是线程安全的。

例如当一个函数F是线程安全函数,但是F调用线程不安全函数G时,同样需要对G进行加锁处理,否则函数F也将不安全。

在《深入理解计算机系统》一书中深入指出了线程不安全函数的分类:

  • 不保护共享产量的函数

  • 保持跨越多个调用状态的函数

  • 返回指向静态变量的指针的函数

  • 调用线程不安全函数的函数

前面介绍的几个例子大部分都是全局变量的不加锁控制相关的,还有两种就是:

  • 函数本次调用依赖于上次调用结果,也就是所谓的跨状态,典型的Linux中的rand()函数;

  • 函数将结果放在一个全局的指针中,典型的gethostbyname、localtime、strtok等;

// 函数原型
struct tm * localtime(const time_t *clock);

/* localtime example */
#include <stdio.h>
#include <time.h>

int main ()
{
time_t rawtime;
struct tm * timeinfo;

time (&rawtime);
timeinfo = localtime (&rawtime);

return 0;
}

在localtime中将结果存放在timeinfo中,这个全局变量可以被任意的线程操作,因此将引发线程不安全。

对于Linux中线程不安全的函数可以查阅:

https://man7.org/linux/man-pages/man7/pthreads.7.html

4. 可重入函数

在理解了线程安全的相关定义和形成原因之后,我们来看下什么是可重入。

先来看看可重入的相关定义:

一个程序可以在任意时刻被中断,然后系统去执行另外一段代码,结束后又调用继续原来的子程序不会出错,则称其为可重入(reentrant或re-entrant)。

从根本上来说:

  • 可重入函数只使用自己栈上的变量,不依赖任何外部数据,可以允许有该函数的多个副本在运行,因为每个调用者产生的函数栈都是相互独立的;

  • 不可重入函数使用了一些系统资源,如果被中断的话,可能会出现问题;

可重入函数又分为两大类:

  • 显式可重入:所有函数的参数都是值传递,并且只使用本地栈变量,那么函数就是显示可重入的,无论如何调用,都是可重入的,是绝对无条件的。

  • 隐式可重入:可重入函数中的一些参数是引用传递,只有在调用线程的时候传递指向非共享数据的指针时,它才是可重入的,是相对有条件的。

可重入函数需要满足以下几个条件:

  • 函数内部不使用静态或者全局数据

  • 函数不返回静态或全局数据,数据的产生都由调用者提供

  • 不调用不可重入函数

从本质上来说,可重入函数实现了算法和数据的分离,函数内部的计算不依赖于外部,不影响也不受外部影响,是一种高效且安全的函数。

可重入函数都是线程安全函数,线程安全不一定是可重入函数。

RZvmaer.png!mobile

不可重入函数可以遵守可重入规则去改造,从而变为可重入函数。

5. 小结

本文从多线程并发编程的一些特征进行阐述,引出了多线程下资源的共享本质。

正因为临界资源和竞态条件的存在,就产生了线程安全问题,在编写多线程程序时一定要考虑线程不安全带来的问题。

在理解线程安全的概念之后进一步引出了可重入函数。

从本质上来说,都是并发环境下由于共享资源带来的问题。

就这样,小黑听完之后虽然一知半解,但也频频点头,一看表快10点了,两个打工人结完账,消失在了去13号线五道口站的夜色中。

EVJbaim.png!mobile

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK