深入解析 Hazard Pointer (中)
看过上篇的朋友,可能会认为:这不就是Smart Pointer么?于是可能写出这样的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
#include<iostream>
#include<thread>
using namespace std;
class SmartPointer
{
public:
SmartPointer(int *p)
{
pointee_ = p;
ref_counts_ = 0;
}
int *pointee_;
int ref_counts_;
};
SmartPointer sp(new int(1));
void Reader()
{
++sp.ref_counts_;
cout<<*(sp.pointee_)<<endl;
--sp.ref_counts_;
}
void Writer()
{
if (sp.ref_counts_ == 0)
delete sp.pointee_;
}
int main()
{
thread t1(Reader);
thread t2(Reader);
thread t3(Reader);
thread t4(Writer);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
|
然而事实上,这样做是错的。其中的race condition请读者自行分析。
那么,Hazard Pointer(HP)和Smart Pointer(SP)有什么区别呢?它们的共同点就是管理对应对象的生命周期,然而这两者有本质的区别,HP是线程安全的,而SP不是。
在HP中,每个读线程维护着自己的HP list,这个list,只由该线程写。因此,它是线程安全的。该list会(可以)被其他线程读。
每个写线程维护自己的retire list,该retire list只由该写线程进行读写。由于写线程可以读其他所有读线程的HP list,这样,差集(在自己的retire list,但是不在所有读线程的HP list里的指针),就是可以安全释放的指针。
而SP,则被多个线程读写,18、19两行也无法做成原子操作,因此,SP和HP有本质的区别,使用SP的程序往往需要搭配使用锁等设施来保证线程安全。
到这里,读者诸君对HP应该有了一定的了解。下集,我们将介绍工业级代码里实现HP的一些思路和技巧,敬请期待。