对象在构造期间不要泄露this指针,即:
因此应该采用二段式构造。
shared_ptr/weak_ptr的计数在主流平台上是原子操作,线程安全级别与内建类型、string和STL容器一样,即:
如果智能指针是对象x的数据成员,而它的模板参数T是个incomplete类型,那么x的析构函数不能是默认的或内联的,必须在.cpp文件里边显式定义,否则会有编译错或运行错。
shared_ptr技术与陷阱
另一个出错的地方是,std::bind会把实参拷贝一份,如果参数是个shared_ptr,那么对象的生命期就不会短于function对象:
class Foo { void doit(); };
shared_ptr<Foo> pFoo(new Foo);
function<void()> func = bind(&Foo::doit, pFoo);
BlockingQueue<shared_ptr<void>>
把对象的析构都转移到专用的线程条件变量使用方式,对于wait端:
muduo::MutexLock mutex;
muduo::Condition cond(mutex);
std::deque<int> queue;
int dequeue() {
MutexLockGuard lock(mutex);
while (queue.empty())
cond.wait();
int top = queue.front();
queue.pop_front();
return top;
}
void enqueue(int x) {
MutexLockGuard lock(mutex);
queue.push_back(x);
cond.notify();
}
使用shared_ptr实现copy on write
typedef std::vector<Foo> FooList;
typedef std::shared_ptr<FooList> FooListPtr;
MutexLock mutex;
FooListPtr g_foos;
void traverse() {
FooListPtr foos;
{
MutexLockGuard lock(mutex);
foos = g_foos;
}
for (auto it = foos->begin(); it != foos->end(); ++it) {
it->doit();
}
}
void post(const Foo& f) {
MutexLockGuard lock(mutex);
if (!g_foos.unique())
g_foos.reset(new FooList(*g_foos));
g_foos->push_back(f);
}
Reactor模式,基于事件驱动的编程模型有本质的缺点,要求事件回调函数必须是非阻塞的。对于涉及网络IO的请求响应式协议,它容易割裂业务逻辑。
必须使用单线程的场合:
是否选择多线程的场合 值得看看
Linux能同时启动几个线程?对于32位机器,一个进程的地址空间是4G,其中用户态能访问3G左右,而一个线程的默认栈大小是10M,因此一个进程大约最多能同时启动300个线程。
__thread只能修饰POD类型,不能修饰class类型,因为无法自动调用构造和析构函数。__thread可以用于修饰全局变量、函数内的静态变量,但是不能用于修饰函数的局部变量或者class的普通成员变量。另外,__thread变量的初始化只能用编译期常量。
多线程程序中,不要使用signal。在服务器程序中,除了需要忽略SIGPIPE,其他都用默认语义。没有别的替代方法下,比如说需要处理SIGCHLD信号,把异步信号转换为同步的文件描述符事件。传统的做法是在signal handler里往一个特定的pipe写一个字节,在主程序中从这个pipe读取,从而纳入统一的IO事件处理框架。现代Linux的做法是采用signalfd把信号直接转换成文件描述符事件。
数独服务器并发模型的选择 值得看看 TODO