8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
学习陈硕老师的《Linux多线程服务端编程》笔记
第1章 线程安全的对象生命期管理
编写线程安全的类是不难的,只要用同步原语保护内部状态即可。但是对象的生与死不能由对象自身拥有的mutex(互斥器)来保护。
1.1 当析构函数遇到多线程
当一个对象可能被多个线程同时看到时,那么对象的销毁时机就会变得模糊不清,可能出现多种竞态条件(race condition):在即将析构一个对象时,对象会不会在另一个线程被析构?
如何保证在执行成员函数期间,对象不会在另一个线程被析构?
在调用某个对象的成员函数之前,如何得知这个对象还活着?它的析构函数会不会碰巧执行到一半?
线程安全的class应该满足三个条件:多个线程同时访问时,其表现出正确的行为;
无论操作系统如何调度这些线程,无论这些线程的执行顺序如何交织;
调用端代码无需额外的同步或其他协调协作。
下面看这个简单的计数器类Counter:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class {
public:
Counter() : value_(0){}
Counter(const Counter &) = delete;
Counter & operator=(const Counter &) = delete;
int64_t value() const;
int64_t getAndIncrease();
private:
int64_t value_;
mutable std::mutex mutex_;
};
int64_t Counter::value () const {
mutex_.lock();
std::lock_guard<:mutex> lck(mutex_, std::adopt_lock);
return value_;
}
int64_t Counter::getAndIncrease() {
mutex_.lock();
std::lock_guard<:mutex> lck(mutex_, std::adopt_lock);
int64_t ret = value_++;
return ret;
}
每个Counter对象有自己的mutex_,因此不同对象不构成锁争用(lock contention)。两个线程有可能同时执行第20行,前提是它们访问的不是同一个Counter对象。
尽管Counter类本身毫无疑问是线程安全的,但如果Counter是动态创建的并通过指针来访问,则race condition仍然存在。
验证代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include
#include
#include "Counter.h"
void display(Counter *pcounter){
if ( pcounter ) {
int64_t v = pcounter->getAndIncrease();
std::cout << v << " ";
delete pcounter;
}
}
int main(){
std::thread threads[4];
Counter *pcounter = new Counter;
for (int i = 0; i < 4; ++i) {
threads[i] = std::thread(display, pcounter);
}
for (auto &t: threads) {
t.join();
}
return 0;
}
1.2 对象的创建很简单
对象在构造的过程中,唯一的要求是在构造期间不要泄露this指针:不要在构造函数中注册任何回调;
也不要在构造函数中把this指针传给跨线程的对象;
即便在构造函数的最后一行也不行。
构造函数执行期间,对象还未完全初始化,this指针泄露给其他对象(其自身创建的子对象除外),那么别的线程有可能访问半成品对象,造成难以预料的后果。即使在最后一行也不要泄露this指针,因为有可能正在构造的对象是一个基类。