Linux多线程服务端编程笔记,C++ 并发编程 —— 《Linux多线程服务端编程》笔记...

好长一段时间没有写文章了,这次总结一下陈硕大大写的《Linux多线程服务端编程》一书第二章的读书笔记。

1.互斥器(mutex)

使用互斥器的基本原则:

用RAII手法封装mutex的创建、销毁、加锁、解锁这四个操作。Java里面的synchronized语句和C#的using语句也有类似的效果,即保证所得生效期间等于一个作用于,不会因异常而忘记解锁。

只用非递归的mutex(即不可重入的mutext)

不手工调用lock()和unlock()函数,一切交给栈上的Guard对象的构造和析构函数负责。

在每次构造Guard对象的时候,思考一路上(调用栈上)已经持有的锁,防止因为锁顺序不同而导致死锁。

进程间通信使用socket,不要使用mutex。

加锁、解锁在同一个线程(RAII会保证)。

别忘记解锁(RAII会保证)。

不要重复解锁(RAII会保证)。

必要的时候可以考虑用PTHREAD_MUTEX_ERRORCHECK来排错。

基本的互斥器的代码如下:

class MutexLock {

public:

MutexLock() {

pthread_mutex_init(&_mutex, NULL);

}

~MutexLock() {

pthread_mutex_destroy(&_mutex);

}

MutexLock& operator=(MutexLock& other) = delete;

MutexLock(MutexLock& other) = delete;

void lock() {

pthread_mutex_lock(&_mutex);

}

void unlock() {

pthread_mutex_unlock(&_mutex);

}

pthread_mutex_t* getRawMutex() {

return &_mutex;

}

private:

pthread_mutex_t _mutex;

};

class MutexGuard {

public:

MutexGuard(MutexLock& mutex)

:_lock(mutex) {

_lock.lock();

}

~MutexGuard() {

_lock.unlock();

}

private:

MutexLock& _lock;

};

2.条件变量(condition variable)

如果需要等待某个条件成立,我们应该使用条件变量。条件变量顾名思义是一个或多个线程等待某个布尔表达式为真,即等待别的线程“唤醒”它。条件变量的学名叫管程(monitor)。Java Object内置的wait()、notify()、notifyAll()是条件变量。

对于wait端应该注意:

必须与mutex一起使用,该布尔表达式的读写接收此mutex的保护

在mutex已经上锁的时候才能调用wait()

把判断布尔条件和wait()放到while循环中

如下是一个简单的Condition实现

class Condition {

public:

Condition(MutexLock& mutex)

:_mutex(mutex) {

pthread_cond_init(&_cond, NULL);

}

~Condition() {

pthread_cond_destroy(&_cond);

}

void wait() {

pthread_cond_wait(&_cond, _mutex.getRawMutex());

}

void notify() {

pthread_cond_signal(&_cond);

}

void notifyAll() {

pthread_cond_broadcast(&_cond);

}

private:

MutexLock& _mutex;

pthread_cond_t _cond;

};

关于Spurious wakeup(虚假唤醒)

虚假唤醒在linux的多处理器系统中,在程序接收到信号前可能会发生。在Windows系统和JAVA虚拟机上也存在。在系统设计时应该可以避免虚假唤醒,但是这会影响条件变量的执行效率,而既然通过while循环就能避免虚假唤醒造成的错误,因此程序的逻辑就变成了while循环的情况。

3.copy-on-write

主要思想是这样的:让读的加锁粒度尽量的小,但当写数据的时候,新的读请求是阻塞的。

typedef std::shared_ptr MapPtr;

MapPtr gData(new Map); // 有一个数据是需要共享的

MapPtr getData() { // 读数据只有这一段是加锁的

MutexGuard guard(mutex);

return gData;

}

// read threads

MapPtr _data = getData();

// process _data....

// write thread

MutexGuard guard(mutex); // 写数据全场加锁

if(!gData.unique()) {

MapPtr tmpData(new Map(gData.get()));

gData.swap(tmpData);

}

// process _data ...

4.小结

记得两年前开始了解C++多线程编程的时候看了陈硕的这本书,工作两年,由于长时间使用Python,对C++已经有些陌生了,更别说C++多线程编程了,今天再次拾起这本书,重读前面两章就当复习了。

接下来我会抽周末看一下《C++ Concurrency In Action》,也会陆续写一些相关的读书笔记。

你可能感兴趣的:(Linux多线程服务端编程笔记)