东阳的学习笔记
CopyOnWriter
1 什么是COW?
COW字面意思写时复制,是一种并发场景下的共享数据策略。
顾名思义,它的基本思想是大家共享同一份数据,读的时候不加锁
,直接读取数据;更新数据时在其拷贝副本上操作
,在这个新拷贝的数据上演绎更新操作,然后再用这个新数据替换老数据
。
2 借 shared_ptr 实现 copy-on-write
在 互斥器 那篇文章中提到了一个死锁的例子。在这里我们来看看如何使用 COW 来解决这个问题:
- 先将数据结构改成:
typedef std::vector<Foo> Foolist;
typedef boost::shared_ptr<Foolist> FooListPtr;
MutexLock mutex;
FooListPtr g_foos;
2.1 read 端
在read端,使用一个栈上局部 FooListPtr 变量作为一个
观察者
,它使得共享变量 g_foos 的引用计数增加
(防止并发修改)。traverse() 函数的临界区是&4~&8,临界区内只读了一次共享变量 g_foos (多线程并发读写 shared_ptr 必须使用 mutex 保护),比原来的写法大为缩短,而且多个线程同时调用 traverse() 也不会堵塞。void traverse() { FooListPtr foos; // 观察者 { MutexLockGuard lock(mutex); foos = g_foos; assert(!g_foos.unique()); } // assert(!g_foos.unique()) 这个断言不成立 for (std::vector<Foo>::const_iterator it = foos.begin(); it != foos.end(); ++it) { it->doit(); } }
write 端
关键看 write 端的 post() 怎么写。
- 如果g_foos.unique()为true,则说明没有线程正在读取,可以放心地
原地修改
- 否则,copy一份,即
COW
void post(const Foo& f) { MutexLockGuard lock(mutex); if (!g_foos.unique()) // 其他线程正在读 { g_foos.reset(new FooList(*g_foos)); printf("copy the whole list"); // } assert(g_foos.unique()); // 没有其他线程正在读,原地操作 foos.push_back(f); }