第2章 线程同步精要
线程同步的四项原则,按重要性排列:
- 1.首要原则是尽量最低限度地共享对象,减少需要同步的场合。一个对象能不暴露给别的线程就不要暴露;如果要暴露,优先设置对象不可更改;实在不行才暴露可修改的对象,并用同步措施来充分保护它。
- 2.其次是使用高级的并发编程构件,如TaskQueue、Producer- Consumer Queue、CountDownLatch等等。
- 3.最后不得已必须使用底层同步原语(primitives)时,只用非递归的互斥器和条件变量,慎用读写锁,不要用信号量。
- 4.除了使用atomic整数之外,不自己编写lock-free代码3,也不要用“内核级”同步原语4 5。不凭空猜测“哪种做法性能会更好”,比如spin lock vs. mutex。
2.1 互斥器(mutex)
单独使用mutex时,我们主要为了保护共享数据。我个人的原则是:
- 使用unique_lock管理mutex,使用构造和析构函数来进行加锁和解锁,而不是自己收到加锁和解锁。
- 只用非递归的mutex(即不可重入的mutex)。
- unique_lock对象一般设置为栈上对象,然后看函数调用栈就能分析用锁的情况,非常便利。【不懂】
次要原则有:
- 不使用跨进程的mutex,进程间通信只用TCP sockets。
- 加锁、解锁在同一个线程,线程a不能去unlock线程b已经锁住的mutex(unique_lock自动保证)。
- 必要的时候可以考虑用PTHREAD_MUTEX_ERRORCHECK来排错。【不懂】
2.1.1 只使用非递归的mutex
在同一个线程里多次对non-recursive mutex加锁会立刻导致死锁,我认为这是它的优点,能帮助我们思考代码对锁的期求,并且及早(在编码阶段)发现问题。
recursive mutex可能会隐藏代码里的一些问题。典 型情况是你以为拿到一个锁就能修改对象了,没想到外层代码已经拿 到了锁,正在修改(或读取)同一个对象呢。如下:
#include "../Mutex.h"
#include "../Thread.h"
#include <vector>
#include <stdio.h>
using namespace muduo;
class Foo
{
public:
void doit() const;
};
MutexLock mutex;
std::vector<Foo> foos;
void post(const Foo& f)
{
MutexLockGuard lock(mutex);
foos.push_back(f);
}
void traverse()
{
MutexLockGuard lock(mutex);
for (std::vector<Foo>::const_iterator it = foos.begin();
it != foos.end(); ++it)
{
it->doit();
}
}
void Foo::doit() const
{
Foo f;
post(f);
}
int main()
{
Foo f;
post(f);
traverse();
}
这个例子说明了什么????
标签:同步,lock,加锁,线程,mutex,精要,Foo From: https://www.cnblogs.com/codingbigdog/p/17279438.html