本文转载自微信公众号《后端技术指南针》,作者指南针氪金入口。转载本文请联系后台技术指南针公众号。1.风起云涌的周五小黑是大白的前同事。现在他们在不同的公司,但是都做后端开发的工作。虽然两人都在北京,但距离也不算太近。一个在望京,一个在中关村,是北京几大IT集群中的第二个。工作之余,两人的闲暇活动也不多,头发当然也不多。宇宙中心五道口,成为了两人的聚集地。眨眼五下,又是周五,空气中仿佛弥漫着明天放假的欢快气氛,当然还有大白和小黑在闹:大白看时间快到了,看了看在线监控报警,没有问题,背上电脑,走出了办公楼。中关村离五道口比较近。我骑着低碳环保的青桔自行车向北行驶到北大东门转五道口。小黑也坐上了13号线,差点被挤成肉饼,但在美食的召唤下,8点准时到达了老地方。大白:黑哥,你什么时候面试腾讯的?它死了吗?你为什么不让我推荐我们的公司呢!小黑:还没呢,等GM开会吧。你们公司亲手撕毁了红树和黑树。大白:就像你一样,那么喜欢穿红毛衣和黑裤子,难怪你不问红黑树的事。对了,腾讯问的是什么?小黑:腾讯的面试总体感觉还是不错的。面试面很广,从操作系统、网络到系统设计,通用组件都会问,而且不偏不倚。大白:那真是太好了。是为了挖掘和试探候选人的技术边界。有关于尼斯的问题吗?告诉我怎么回事儿。小黑:有一个问题就是我的盲区。我将看看哪些是线程安全的,哪些是可重入的,并解释原因。大白:哦,这是考察对线程安全函数和可重入函数的理解。那你怎么回答的?小黑:靠,我说我不太会,然后跳到下一题。你为什么不告诉我?我先做一个!小黑说完就灌满了啤酒,大白扶了扶自己几年没换的眼镜,开始和小黑讨论什么是线程安全和可重入。2.多线程与并发在用C++开发的服务器程序中,多线程仍然是主流。一般来说,都会有一个线程池来处理接收到的请求,可以有效的提供服务器并发和CPU利用率。然而,多线程也是一把双刃剑。在单线程模式下,一切都是那么的单调和稳定,所有的资源都是我自己的,我的资源我做主。多线程模式下,一个进程下加载多个线程,每个线程共享除部分资源外的大部分系统资源。多线程共享的进程资源:内存、文件描述符、地址空间、全局数据……每个线程独有的资源:线程寄存器、线程栈、线程ID、错误返回码、信号屏蔽码……敲黑板划重点:1.进程是系统资源分配和调度的基本单位,线程是CPU调度和调度的基本单位;2、进程是线程的载体,进程有独立的地址空间,所有线程共享进程的地址空间;3、进程是系统资源的大股东,线程基本不拥有系统资源,只占用少量运行必不可少的资源,如程序计数器、一组寄存器、调用栈等;同一个进程中的多个线程有点像共享。大家共享大部分资源,又独占一小部分资源,相互影响。但是单进程单线程都是租用的,独占了所有的资源,不影响任何人。掌握了多线程中资源共享和相互影响的特点后,再看线程安全和可重入性就容易多了。3.什么是线程安全?计算机中所谓的安全,多是指结果的正确性和可预见性。前面我们知道,虽然多线程可以提高并发性,但是多个线程会共享很多资源,比如写入全局数据。在这种情况下,就需要额外的干预,否则会造成乱序的结果。线程安全是指在多个线程并行执行共享数据的过程中,能够正常正确执行,不会出现数据污染等意外情况。否则,称为线程不安全。通俗的说,如果线程是安全的,你怎么跑都不会乱,但是如果线程不安全,你跑起来可能就各种乱七八糟。所以可能的线程不安全的根本原因是:共享数据和共享数据是可变的。这些共享数据包括全局变量、局部静态变量等,每个线程都可能对这些数据进行操作,操作的结果会影响到其他线程。我们还经常提到另一个名词:线程安全函数/线程安全类。线程安全函数的一些特点:没有共享数据,都是本地数据;有写共享数据,但已被锁定,可实现多线程同步调用;有读无写共享数据,无需加锁;从图中可以看出:同一个进程中有四个工作线程;public函数A只进行打印操作,无论任何线程何时调用,结果都是确定的、正确的,是线程安全的函数;public函数B使用全局变量Count加1,但是没有进行锁同步,所以结果不确定,是线程不安全的函数;公共函数C使用全局变量Factor,自增2,使用互斥锁进行同步,保证结果的正确性,是线程安全的函数;在编写多线程程序时,如果多个线程参与操作一个公共函数,如果该函数本身不是线程安全的。比如当一个函数F是线程安全的函数,但是F调用了一个线程不安全的函数G,G也需要加锁,否则函数F也会不安全。《深入理解计算机系统》中深入指出了线程不安全函数的分类:不保护共享收益的函数跨多个调用保持状态的函数返回指向静态变量的指针的函数调用线程不安全函数的函数最前面的例子和全局变量的解锁控制有关,还有另外两种:函数的本次调用取决于上次调用的结果,也就是所谓的跨状态,典型的Linux中的rand()函数;函数将结果放在一个全局指针中,典型的有gethostbyname、localtime、strtok等;//函数原型structtm*localtime(consttime_t*clock);/*localtimeexample*/#include
