muduo
前言:
对于muduo库,我觉得,光linux多线程上提到的一些实现,还是不够的,在base/里面,还有/net里面提供了很多不错的实现,值得去学习,暑假算是看看muduo的百分之八十的源码,并对其进行了一次实现,(剩下的在最近总结的时候,也会开始看看,并实现一遍),对于muduo库,简单谈谈自己对其实现的理解。
用RaiI管理你的锁
Posix Thread内定义的一系列的mutex函数,但是是基于C语言的,用起来很不方便,需要不断地初始化,销毁p,我们要对其进行一定程度的封装,使得我们不必重复的写初始话,销毁操作,专注在使用上。
muduo对锁进行了封装,分别是Mutex、MutexGruad、Condition、countdownlatch。
Mutex的实现
class MutexLock : boost::noncopyable { public: MutexLock():holder_(0) { MCHECK(pthread_mutex_init(&mutex_, NULL)); } ~MutexLock() { assert(holder_ == 0); MCHECK(pthread_mutex_destroy(&mutex_)); } bool isLocketByThisThread() const { return holder_ == CurrentThread::tid(); } void assertLocked() const { assert(isLocketByThisThread()); } void lock() { MCHECK(pthread_mutex_lock(&mutex_)); assignHolder(); } void unlock() { unassignHolder(); MCHECK(pthread_mutex_unlock(&mutex_)); } pthread_mutex_t* getPthreadMutex() /* non-const */ { return &mutex_; } private: void unassignHolder() { holder_ = 0; } void assignHolder() { holder_ = CurrentThread::tid(); } private: pthread_mutex_t mutex_; pid_t holder_; // 如果不是同一个线程的加锁和解锁则会失败 };
如果单单只是这样实现mutex,还是太过于麻烦,而且这个锁,我们只能在函数内自动获取自动产生,然后自动析构,会有一定的性能损失。
下面实现了MutexLockGurad用于自动获取锁,加锁和解锁。
class MutexLockguard : boost::noncopyable { public: explicit MutexLockGuard(MutexLock& mutex):mutex_(mutex) { mutex_.lock(); } ~MutexLockGuard() { mutex_.unlock(); } private: MutexLock& mutex_; };
还有需要注意的就是,在实现的时候,定义了两个宏
// 用宏的方式检查返回值是否为0, #ifdef CHECK_PTHREAD_RETURN_VALUE #ifdef NDEBUG __BEGIN_DECLS extern void __assert_pERROR_fail (int errnum, const char *file, unsigned int line, const char *function) __throw __attribute__ ((__noreturn__)); __END_DECLS #endif #define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret); \ if (__builtin_expect(errnum != 0, 0)) \ __assert_perror_fail (errnum, __FILE__, __LINE__, __func__);}) #else // CHECK_PTHREAD_RETURN_VALUE // 定义一个ret类型的errnum,然后判断errnum是否==0, // (void)(value)作用仅仅就是以显眼的方式让编译器不要给出参数未被使用的警告 #define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret); \ assert(errnum == 0); (void) errnum;}) #endif // CHECK_PTHREAD_RETURN_VALUE // 防止错误的用法 #define MutexLockGuard(x) error "Missing guard object name"
没有注释的代码,里面有些小问题,我们可以总结一下
__builtin_expect
提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
函数声明如下
long __builtin_expect(long exp, long c);
exp 为一个整型表达式, 例如: (ptr != NULL)
c 必须是一个编译期常量, 不能使用变量
返回值等于 第一个参数 exp
这个函数的语义是:你期望exp表达式的值等于常量c,从而GCC为你优化程序,将符合这个条件的分支放在合适的地方。
__assert_perror_fail (errnum, __FILE__, __LINE__, __func__)
最后会按照FILE :LINE : func : errnum打印出错误,和assert一样,都在编译时候执行,但是好处在于它能按照格式打印出错误在哪里,便于快速排查出错误。
Condition的实现
如果直接使用我们上面实现的Mutex,会出现死锁。
因为在我们使用Conditon的wait的时候:
-
释放Mutex
-
阻塞等待
当其它线程调用pthread_cond_signal或pthread_cond_signal,它会重写获取锁。
这个时候,pthread_cond_wait势必会改变pthread_mutex_t和MutexLock:holder的一致性。所以需要在调用pthread_cond_wait的前后添加一些代码去相应的修改*MutexLock::holder,也就是分别调用MutexLock::unassignHolder和MutexLock::assignHolder。MutexLock::UnassignGuard类的作用,就是利用RAII简化对MutexLock::unassignHolder和MutexLock::assignHolder的调用。*
所以在Mutex中我们需要加入unassignGuard类
class Mutex { ... private: friend class Condition; class UnassignGuard : boost::noncopyable { public: UnassignGuard(MutexLock& owner) : owner_(owner) { owner_.unassignHolder(); } ~UnassignGuard() { owner_.assignHolder(); } private: MutexLock& owner_; }; ... };
这样我们就可以实现Conditon了
class Condition : boost::noncopyable { public: explicit Condition(MutexLock& mutex) : mutex_(mutex) { MCHECK(pthread_cond_init(&pcond_, NULL)); } ~Condition() { MCHECK(pthread_cond_destroy(&pcond_)); } void wait() { MutexLock::UnassignGuard ug(mutex_); //先将holder_清零,防止出现死锁 MCHECK(pthread_cond_wait(&pcond_, mutex_.getPthreadMutex())); // 析构的时候,会将holder_恢复 } // returns true if time out, false otherwise. bool waitforSeconds(double seconds); void notify() { MCHECK(pthread_cond_signal(&pcond_)); } void notifyAll() { MCHECK(pthread_cond_broadcast(&pcond_)); } void waitForSeconds(double seconds) { struct timespec abstime; // FIXME: use CLOCK_MONOTONIC or CLOCK_MONOTONIC_RAW to prevent time rewind. clock_gettime(CLOCK_REALTIME, &abstime); const int64_t kNanoSecondsPerSecond = 1000000000; int64_t nanoseconds = static_cast<int64_t>(seconds * kNanoSecondsPerSecond); abstime.tv_sec += static_cast<time_t>((abstime.tv_nsec + nanoseconds) / kNanoSecondsPerSecond); abstime.tv_nsec = static_cast<long>((abstime.tv_nsec + nanoseconds) % kNanoSecondsPerSecond); MutexLock::UnassignGuard ug(mutex_); return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime); } private: MutexLock& mutex_; pthread_cond_t pcond_; };
CountDownLatch的实现
我们有时对Condition类上面再套一层封装来使用,他就是我们的CountDownLatch类。通过倒计时计数器的方式,设置计数。倒计时未完,CountDownLatch的内部一直处于
Condition类的wait()状态。倒计时完毕,唤醒cond,程序正常执行。所以我们可以给某件即将发生的事设置一个条件,比如至少5个满足才会触发该事件,那么计数值设为5,
等到倒计时为0时,CountDownLatch类就可以让你顺利的去执行该事件了。
所以它主要有两个用法:
1.既可以用于所有子线程主动等待主线程发起“起跑”
2.也可以用于主线哼等待子线程初始化完毕才开始工作
class CountDownLatch : boost::noncopyable { public: explicit CountDownLatch(int count) : mutex_(), condition_(mutex_), count_(count) { } void wait() { MutexLockGuard lock(mutex_); while (count_ > 0) { condition_.wait(); } } void countDown() { MutexLockGuard lock(mutex_); --count_; if (count_ == 0) { condition_.notifyAll(); } } int getCount() const { MutexLockGuard lock(mutex_); return count_; } private: mutable MutexLock mutex_; Condition condition_; int count_; };
相关阅读
一、学习经历 如果想看我的学习心得、项目截图、写ExtJs专栏的目的,可以看一下以下这篇文章: https://blog.csdn.net/xiaozhegaa/a
sprintf( s, " threads=%d", p->i_threads );最大值128 X264_THREAD_MAX。如果没有设置threads参数,x264根据当前CPU核数以及slice
很多初学C语言的同学可能遇到的首要问题,就是选择编译器,用什么编程软件?然而通过了解之后发现有那么多编程软件,什么VC6.0,Dev ,CodeBl
现在的这个社会,photoshop也就是我们所说的ps已经被大部分工作的人所使用了,ps能用在工作的很多方面。正是因为它的广泛运用,很多人
背景: 在开发项目中,我们公司的产品要给别人用,除了把代码封装成静态库之外,还需要把你的图片等资源封装成bundle包。 这样,别人拿