交易系统开发技能及面试题之多线程并发编程(Multithreading)

概要

交易系统开发技能系列的第二部分内容:并发编程

多进程、多线程编程是大型系统开发必不可少的技能,这部分内容涉及各种各样的锁,死锁,线程安全思考,以及生产者消费者模式: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空转。

Q5 线程池

当我们有大量异步任务需要使用线程执行时,频繁创建销毁线程的开销很大,而且线程也不是无上限越多越好,通过实践得出一般线程数为cpu * 2的效果较佳。为了处理大批量的异步任务,生产中通常采用线程池的方式,这也是一种生产者-消费者模式,线程池通过预创建线程,以及一个同步队列,应用层将任务丢到同步队列中,由线程池分配一个空闲线程进行处理,减少了线程创建销毁开销的同时,也可以对应用层进行任务限流。
以下采用c++ 实现线程池
其中使用到的同步队列用于接收任务,主要是封装了底层的list操作,保证线程安全

#include<list>
#include<thread>
#include<functional>
#include<atomic>
//同步队列
#include"SyncQueue.hpp"

using Task = std::function<void()>;
const int MaxTaskCount = 100;
class ThreadPool {
   
private:
    //池里维护的线程
    std::list<std::shared_ptr<std::thread>> _threadgroup;
    //处理的任务数
    SyncQueue
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值