交易系统开发技能之并发编程面试题
Posted BBinChina
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了交易系统开发技能之并发编程面试题相关的知识,希望对你有一定的参考价值。
概要
交易系统开发技能系列的第二部分内容:并发编程
多进程、多线程编程是大型系统开发必不可少的技能,这部分内容涉及各种各样的锁,死锁,线程安全思考,以及生产者消费者模式:spsc、mpmc等等。
Q1 mutex 与 Binary semaphore的区别
mutex(互斥),我们常说的锁机制,其目的是用来保护临界区资源在同一时刻只有一个操作在执行,获得锁的线程有操作权,而锁只能由拥有者释放。
Binary semaphore是Semaphore的一种,其变量值只有0或者1。最初为1,某个资源操作者通过wait将值从1变为0,调用signal时,值变为0。而调用signal由其他任何的线程或者进程执行。
mutex、binary semaphore都是用于操作临界区资源,但最大的区别在于释放者的不同,metux强调所有权,semaphore采用通知机制。
Q2 线程安全函数 与 可重入(re-entrant)函数的区别
线程安全函数在于多线程环境下执行返回结果正确,函数内数据为竞态数据,需要用同步机制保证同一时间只有一个线程在调用。
可重入函数在于函数执行过程中阻断后重新回到调用栈时,函数执行结果不变,比如递归函数。
先看以下几个列子:
1、
int tmp;
int add(int a)
tmp = a;
return tmp + 10;
tmp在这里是多线程竞争资源,因为没有通过锁处理,所以其在多线程环境下不是线程安全的。
假设当前调用函数add(1),在执行tmp + 10指令的时中断去调用add(2)后回到当前调用栈,tmp变为了2,那么2 + 10变成了12,与原本应该获得的11值不符合,那么叫不可重入函数。
2、
thread_local int tmp;
int add(int a)
tmp = a;
return tmp + 1;
使用线程变量保证不同线程执行结果一致,为线程安全。
但依然不可重入
3、
int tmp;
int add(int a)
tmp = a;
return a + 1;
线程不安全
可重入,因为函数调用计算结果为a+1,a在回到原调用栈时不变
4、
int add(int a)
return a + 10;
线程安全,可重入
Q3 死锁 与 活锁的区别
死锁的概念比较常见,比如两个正在操作竞态数据的任务相互依赖,比如 线程A1 获取到了锁L1 等待 锁L2的释放,而持有锁L2的线程A2正在等待A1释放锁L1,彼此都阻塞住,那么即形成了死锁的情况,除了重启进程清除指令,死锁永远无法解开。
为了解锁上述的死锁问题,我们可以通过尝试获得锁的方式,比如上述问题:A1尝试获取L2的锁,当获取不到时,可以回滚释放L1,当A2尝试获取L1成功时,A2可以正常执行,这样就解决了死锁。但是有可能A2在尝试获取L1时没成功,而A1也回滚了,那么两个任务都得不到执行,再重复进入尝试获取锁的状态,尽管没有阻塞,但也形同"阻塞",这就是 活锁,只不过它可能某个cpu执行周期时尝试锁的时候错开了。
解决活锁的方式可以通过线程在尝试获取锁时设置一个超时随机值。
Q4 自旋锁
当一个线程在等待mutex时,系统会保存线程上下文之后是线程进入睡眠状态,当锁被释放时,唤醒睡眠中的等待线程。对于追求高性能来说,线程阻塞唤醒的开销是可优化(可压榨)的点,为了减少这个开销,我们可以采用自旋锁的方式,在线程上一直尝试获取锁。但同时也引入了一个问题,一直尝试占用了cpu,考虑使用自旋锁应该是锁路径极短的情况下,避免cpu空转。
以上是关于交易系统开发技能之并发编程面试题的主要内容,如果未能解决你的问题,请参考以下文章