高效並發編程
並發編程的基本模型包括,通過消息機制來管理運行順序的message passing, 通過互斥保護共享的shared memory。
線程同步的基本原則
- 最低限度共享變量,考慮使用immutable對象
- 盡量減小鎖粒度
- 互斥器和條件變量足以完成絕大多數任務,盡量使用高層的封裝
- 避繁就簡,讀寫鎖 信號量 可重入鎖 ,慎用。
關於死鎖
- RAII 控制鎖區間
- 注意鎖的獲取順序
Copy On Write 減小鎖粒度
只讀的情況下用shared_ptr 輕量級共享數據
在發生修改的情況下,對發生次數較小的情況做數據拷貝,比如說我們的數據每秒鍾被讀取一百次,平均每十秒添加一次數據,那么就針對添加數據的情況做Copy on write,添加數據時如果數據被使用,就copy一份!(由於使用數據線程保存了一份shared_ptr,所以沒有問題)
我們來看一個例子
class Foo
{
public:
void doit() const;
};
typedef std::vector<Foo> FooList;
typedef boost::shared_ptr<FooList> FooListPtr;
FooListPtr g_foos;
MutexLock mutex;
void post(const Foo& f)
{
printf("post\n");
MutexLockGuard lock(mutex);
if (!g_foos.unique())//有其他線程在讀,重新拷貝一份
{
g_foos.reset(new FooList(*g_foos));
printf("copy the whole list\n");
}
assert(g_foos.unique());
g_foos->push_back(f);
}
void traverse()
{
FooListPtr foos;
{
MutexLockGuard lock(mutex);
foos = g_foos;
assert(!g_foos.unique());
}
// assert(!foos.unique()); this may not hold
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()
{
g_foos.reset(new FooList);
Foo f;
post(f);
traverse();
}