• 《Linux多线程服务端编程》笔记——线程同步精要


    并发编程基本模型
    message passing和shared memory。

    线程同步的四项原则

    • 尽量最低限度地共享对象,减少需要同步的场合。如果确实需要,优先考虑共享 immutable 对象。
    • 使用高级的并发编程构件,如TaskQueue、Producer-Consumer Queue、CountDownLatch等等。
    • 不得已必须使用底层同步原语(primitives)时,只用非递归的互斥器和条件变量,慎用读写锁,不要用信号量。
    • 除了使用 atomic 整数之外,不自己编写 lock-free 代码,也不要用“内核级”同步原语。不凭空猜测“哪种做法性能会更好”,比如 spin lock vs. mutex。

    互斥器的使用

    • 用 RAII 手法封装 mutex 的创建、销毁、加锁、解锁这四个操作。保证锁的生效期间等于一个作用域(scope)。
    • 只用非递归的 mutex(即不可重入的 mutex)。
    • 不手工调用 lock() 和 unlock() 函数,一切交给栈上的 Guard 对象的构造和析构函数负责(Scoped Locking)。
    • 在每次构造 Guard 对象的时候,思考一路上(调用栈上)已经持有的锁,防止因加锁顺序不同而导致死锁。

    条件变量的使用

    • 对于 wait() 端:
      必须与 mutex 一起使用,该布尔表达式的读写需受此 mutex 的保护。
      在 mutex 已上锁的情况下才能调用 wait()。
      把判断布尔表达式和 wait() 放在 while 循环中。

    • 对于 signal/broadcast 端:
      不一定要在 mutex 已上锁的情况下调用 signal(理论上)。
      在 signal 之前一般要修改布尔表达式。
      修改布尔表达式通常需要用 mutex 保护(至少用作 full memory barrier)。
      broadcast 通常用于表明状态变化,signal 通常用于表示资源可用。

    • 虚假唤醒(spurious wakeup),Linux 中 futex 慢速系统调用被信号打断返回 -1,wait 返回了。

    读写锁与信号量的使用

    • 从正确性方面来说,一种典型的易犯的错误是在持有 reader lock 的时候修改了共享数据。
    • 从性能方面来说,读写锁不见得比普通 mutex 更高效。
    • reader lock 可能允许提升为 writer lock,也可能不允许提升。
    • 通常 reader lock 是可重入的,writer lock 是不可重入的。
    • 信号量不是必备的同步原语,因为条件变量配合互斥器可以完全替代其功能,而且更不易用错。

    参考:《Linux多线程服务端编程》。

  • 相关阅读:
    PyQt5 控件学习(一个一个学习之QCommandLinkButton)
    多任务--线程
    PyQt5 控件学习(一个一个学习之QPushButton)
    PyQt5 控件学习(一个一个学习之QAbstractButton)
    再测我心中的事
    花了两天时间,整理了代码,封装了逻辑
    我现在发现,我写代码有严重的问题
    2014年8月2日0时13分22秒
    2014年8月2日15时13分4秒
    交警与货车司机
  • 原文地址:https://www.cnblogs.com/shuaihanhungry/p/5798012.html
Copyright © 2020-2023  润新知