多线程同步

读陈硕先生的《moduo多线程服务器编程》第二章线程同步精要,做下笔记。

编程概要

  1. 首要原则是尽量最低限度共享对象,减少需要同步的场合。
  2. 其次是使用高级的并发编程组件,如TaskQueue、Producer-Consumer Queue、CountDownLatch等. [链接](https://github.com/chenshuo/muduo
  3. 最后不得已必须使用底层同步原语时,只用非递归的互斥器和条件变量,慎用读写锁,不要用信号量。
  4. 除了使用atomic整数之外,不自己写lock-free代码,也不要用“内核级”同步原语。

互斥器(mutex)

互斥器用于保护临界区,任何一个时刻最多只能有一个线程在此mutex划出的临界区内活动,单独使用mutex时,主要是为了保护共享资源。编程原则:
1. 用RAII手法封装mutex创建,销毁,加锁,解锁这四个操作。具体代码见最后。
2. 只用非递归的mutex(不可重入的mutex)。因为少用一个计数器,比递归的mutex略快一点,但主要还是为了设计意图,不是为了性能考虑。在同一个线程中多次对非递归mutex加入会立刻死锁,能帮助我们及早发现问题。
3. 不手工调用lock()和unlock()函数,一切交给栈上的Guard对象构造函数和析构函数负责。
4. 每次构造Guard对象,思考一路上(调用栈上)已经持有的锁,防止因加锁顺序不同而导致死锁。

次要原则:
1. 不使用跨进程的mutex,进程间通信只用TCP sockets。
2. 加锁、解锁都在同一个线程,线程a不能去unlock线程b已经锁住的mutex。
3. RAII保证解锁与不重复解锁。
4. 必要的时候可以考虑PTHREAD_MUTEX_ERRORCHECK来排错。

note: Linux的Pthreads mutex 采用 futex 实现,不必每次加锁、解锁都陷入内核。

条件变量(cond)

如果需要等待某个条件成立,应该使用条件变量,学名管程。
对于wait端:
1. 必须和mutex一起使用,该布尔表达式的读写需受此mutex保护。
2. 在mutex已上锁的时候才能调用wait()。
3. 把判断布尔条件和wait()放到while循环中。

例子1:
链接

例子2:

muduo::MutexLock mutex;
muduo::Condition cond(mutex);
std::deque<int> queue;

int dequeue(){
    MutexLockGuard lock(mutex);
    while(queue.empty()){
        cond.wait();
    }
    int top = queue.front();
    queue.pop_front();
    return pop;
}

note:必须使用while循环来等待条件变量,不是使用if语句,原因是spurious wakeup。

对于signal/broadcast端:
1. 不一定要在mutex已上锁的情况下调用signal
2. 在signal之前一般要修改布尔表达式。
3. 修改布尔表达式通常要用mutex保护。
4. broadcast通常用于表明状态变化,signal通常用于表示资源可用。

例子1:
链接

例子2:

void enqueue(int x){
    MutexLockGuard lock(mutex);
    queue.push_back(x);
    cond.notify();
}

note:互斥器和条件变量构成了多线程编程的全部必备同步源于,用它们即可完成任何多线程同步任务,二者不可相互替代。

不用读写锁和信号量

  1. 正确性上:易发生在持有read lock时候修改了共享数据,这种错误的后果跟无保护并发读写共享数据时一样的。
  2. 性能上:读写锁不见得比普通mutex更高效,如果临界区很小,锁竞争不激烈,mutex往往更快。
  3. 通常reader lock是可重入的,writer lock是不可重入的。但为了防止writer饥饿,writer通常会阻塞后来的reader lock,因此reader lock在重入的时候可能死锁。

不用信号量:
1. 使用条件变量配合互斥器可以完全替代信号量,而且不易出错。
2. 信号量增加了程序设计的负担和出错的可能。

代码

MutexLock:
链接

MutextLockGuard:
链接

Condition:
链接

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值