- 并发编程有两种基本模型:message passing & shared memory。
- 线程同步的四项原则:
- 首要原则是最低限度的共享对象,减少需要同步的场合,一个对象能不暴露给别的线程就不要暴露,如果要暴露,优先考虑immutable对象,实在不行才暴露可修改的对象,并用同步措施来充分保护它。
- 其次是使用高级的并发编程构件,如TaskQueue,Producer-Comsumer Queue,CountDownLatch等等。
- 最后不得已必须使用底层同步原语时,只用不可重入的互斥器和条件变量,慎用读写锁,不要用信号量。
- 除了使用atomic整数之外,不自己编写lock-free代码,也不要用“内核级”同步原语。不凭空猜测“哪种做法性能更好”,比如spin lock vs mutex。
-
使用互斥器的原则:用RALL手法封装mutex的创建,销毁,加锁,解锁这四个操作,只用不可重入的mutex,不手工调用lock()和unlock()函数,一切交给栈上的Guard对象的构造和析构函数负责。Guard的生命期正好等于临界区,Scoped Locking。在每次构造Guard对象的时候,思考一路上(调用栈上)已经持有的锁,防止因加锁顺序不同而导致死锁。次要原则:不使用跨进程的mutex,进程间通信只用TCP sockets。
- 只用不可重入的mutex。
- 条件变量的使用:
- 必须和mutex一起使用,条件变量的表达式的读写需受此mutex保护
- 在mutex已上锁的时候才能调用wait()。
- 把判断条件变量和wait放到while循环中。
-
互斥器和条件变量构成了多线程编程的全部必备同步原语,用它们即可完成任何多线程同步任务,二者不能相互替代。
- 不要用读写锁和信号量
- 线程安全的singleton实现用pthread_once_t来保证执行一次实例化操作。
-
我提倡正确加锁而不是自己编写lock-free算法,很多人误认为用锁会让程序变慢,其实真正影响程序性能的不是锁,而是锁争用。
- 关于race condition ,chatgpt给出了延迟析构等解决构想,采用的是条件变量和mutex的协作。