我是靠谱客的博主 慈祥钢笔,最近开发中收集的这篇文章主要介绍muduo库源码阅读之MutexLock、MutexLockGuard、Contidion、CountDownLatch分析,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
一、互斥锁封装:Mutex.h
github地址:https://github.com/chenshuo/muduo/blob/master/muduo/base/Mutex.h
1.1 MutexLock类
互斥量是线程同步常用的变量,但在实际中一般都是使用封装的函数,这样便于操作。
MutexLock封装临界区(critical section),这是一个简单的资源类,用RAII手法封装互斥器的创建与销毁。
共有两个变量,mutex_是互斥量,holder_是用来表示给互斥量上锁线程的tid。
在构造函数中初始化互斥量mutex_和holder_(0),在析构函数中给销毁mutex_。对我接口根据名字很容易看出用法。
bool isLockByThisThread()是用来检查是否是当前线程给这个MutexLock对象加锁的,用于程序断言,原理为比较holder_和 CurrentThread::tid()的值。
assingnHolder和unassignHolder分别在上锁时给holder_赋值,解锁是给holder_置零。assignHolder在上锁后调用,而unassignHolder在解锁前调用。
在构造函数中初始化互斥量mutex_和holder_(0),在析构函数中给销毁mutex_。对我接口根据名字很容易看出用法。
bool isLockByThisThread()是用来检查是否是当前线程给这个MutexLock对象加锁的,用于程序断言,原理为比较holder_和 CurrentThread::tid()的值。
assingnHolder和unassignHolder分别在上锁时给holder_赋值,解锁是给holder_置零。assignHolder在上锁后调用,而unassignHolder在解锁前调用。
void lock()和void unlock(),加锁和解锁,
仅供Conditio调用。在加锁时holder_被赋予 CurrentThread::tid(),解锁时被置0。
pthread_mutex_t* getPthreadMutex()可以返回指向类对象中互斥量的指针,在类外对互斥量操作,这个仅供Conditio调用。
pthread_mutex_t* getPthreadMutex()可以返回指向类对象中互斥量的指针,在类外对互斥量操作,这个仅供Conditio调用。
这个
UnassignGuard类中有一个MutexLock对象的引用,在其构造函数调用unassignHolder,析构函数中assignHolder,这个是为条件变量
pthread_cond_wait()调用时设计的。
MutexLock::lock()里调用
assingnHolder()函数给holder_赋值,而MutexLock::unlock()里调用unassignHolder()函数给holder_置零。需要保持
MutexLock::holder_与pthread_mutex_t实例内部状态的一致性。在调用pthread_cond_wait()会解锁MuteLock,等待条件,解锁那就要给给holder_置零,pthread_cond_wait()返回后重新加锁同时要给给holder_赋值,否则MutexLock::holder_与pthread_mutex_t实例内部状态的一致性就被破坏了。所以需要在调用pthread_cond_wait的前后添加一些代码去相应的修改MutexLock::holder_,也就是分别调用MutexLock::unassignHolder和MutexLock::assignHolder。MutexLock::UnassignGuard类的作用,就是利用RAII简化对MutexLock::unassignHolder和MutexLock::assignHolder的调用。
在调用mutex的linux api时,检查返回值,如lock()函数。
void lock()
{
MCHECK(pthread_mutex_lock(&mutex_));
assignHolder();
}
检查返回值的意义在于防止ENOMEM 之类的资源不足情况,这一般只可能在负载很重的产品程序中出现。一旦出现这种错误,程序必须清理现场并主动退出,否则会莫名其妙的崩溃,给事后调查造成困难。可用non-debug的assert,或借鉴google-glog的CHECK()宏
定义的MCHECK宏
#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
#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);
assert(errnum == 0); (void) errnum;})
#endif // CHECK_PTHREAD_RETURN_VALUE
1.2 MutexLockGuard类
在使用mutex时,有时会忘记给mutex解锁,为了防止这种情况发生,常常使用RAII手法(资源的地点是构造函数,释放点是析构函数)
。MutexLockGuard就是为此设计的。
内部有一个MutexLock变量的引用。
MutexLockGuard封装临界区的进入和退出,即加锁和解锁。MutexLockGuard一般是个栈上对象,它的作用域刚好等于临界区域。通过传递Mutex引用,在构造函数中对其加锁;
在析构函数解锁。
// Prevent misuse like:
// MutexLockGuard(mutex_);
// A tempory object doesn't hold the lock for long!
#define MutexLockGuard(x) error "Missing guard object name"
定义了上面的一个宏,这个宏的作用是防止程序里出现如下错误:
void doit()
{
MutexLockGuard(mutex); //漏写变量名,产生一个临时对象又马上销毁了,结果没锁住临界区
//正确写法是 MutexLockGuard lock(mutex);
//临界区
}
二、条件变量的封装:Condition.h
github地址:
https://github.com/chenshuo/muduo/blob/master/muduo/base/Condition.h
https://github.com/chenshuo/muduo/blob/master/muduo/base/Condition.cc
conditon构造函数中传递MutexLock的引用
void wait()
{
MutexLock::UnassignGuard ug(mutex_);//这里先给mutex_的holder_置零。在等其析构时会给holder_赋值
MCHECK(pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()));//给mutex_解锁
}
pthread_cond_wait(&pcond_, mutex_.getPthreadMutex())会先给mutex_解锁,然后等待条件。这两步是原子操作。在条件成立后,它会给mutex_加锁,然后返回,这两步也是原子操作。
在这里可以看到MutexLock::UnassignGuard的应用。
在执行等待之前,使用UnassignGuard的构造函数将mutex_的holder清空(因为当前线程会休眠,暂时失去对mutex_的所有权)。接着调用pthread_cond_wait等待其他线程的通知。当其他某个线程调用了notify/notifyAll时,当前线程被唤醒,接着在wait返回时,UnassignGuard的析构函数自动将mutex_的holder设置为当前线程。MutexLock::UnassignGuard类的作用,就是利用RAII简化对MutexLock::unassignHolder和MutexLock::assignHolder的调用。
时间函数: time( )--秒级, gettimeofday---微妙 , clock_gettime --纳秒 , _ftime ---毫秒
// returns true if time out, false otherwise.
bool muduo::Condition::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 = 1e9;
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);
}
如果一个class要包含MutexLock和
Condition,请注意它们的声明顺序和初始化顺序,mutex_应该先于condition_构造,并作为后者的构造参数。
三、倒计时类封装:CountDownLatch
github地址:
https://github.com/chenshuo/muduo/blob/master/muduo/base/CountDownLatch.h
https://github.com/chenshuo/muduo/blob/master/muduo/base/CountDownLatch.cc
这个是参考Java的一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。既可以用于所有子线程等待主线程发起 “起跑”,也可以用于主线程等待子线程初始化完毕才开始工作
condition 与mutex都为自己私有的,不是外边传进来的。
发号通知-报数的人,不断调用CountDownLatch::countDown( ) 减数,减到0,就通知其他等待的人
等待命令的人,调用CountDownLatch::wait()等待
但是所有人都要调用同一个CountDownLatch示例(即统一condition、mutex)
注意mutext_是mutable类型,因为 getCount成员函数是const类型,不能修改此实例,但是getCount用到了锁,要修改mutex_,为了可以只修改锁,而不修改其他变量,所以把锁表明mutable类型
发号通知-报数的人,不断调用CountDownLatch::countDown( ) 减数,减到0,就通知其他等待的人
等待命令的人,调用CountDownLatch::wait()等待
但是所有人都要调用同一个CountDownLatch示例(即统一condition、mutex)
注意mutext_是mutable类型,因为 getCount成员函数是const类型,不能修改此实例,但是getCount用到了锁,要修改mutex_,为了可以只修改锁,而不修改其他变量,所以把锁表明mutable类型
以一个例子说明,主线程创建了2个子线程,这两个子线程调用的函数阻塞在wait()上。当主线程调用countDown()后,子线程才运行。
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Thread.h>
#include <boost/bind.hpp>
#include <vector>
#include <stdio.h>
using namespace muduo;
CountDownLatch latch_(1);
void Function()
{
latch_.wait();//wait for latch_ countDown
printf("Thread ID=%d, Name=%sn", CurrentThread::tid(), CurrentThread::name());
}
int main()
{
Thread t1(boost::bind(Function), "Thread 1");
Thread t2(boost::bind(Function), "Thread 2");
t1.start();
t2.start();
printf("main thread running, before countDownn");
latch_.countDown();
sleep(3);//wait for thread t1, t2
printf("main thread running, after countDownn");
t1.join();
t2.join();
return 0;
}
运行结果:
main thread running, before countDown
Thread ID=6994, Name=Thread 2
Thread ID=6993, Name=Thread 1
main thread running, after countDown
Thread ID=6994, Name=Thread 2
Thread ID=6993, Name=Thread 1
main thread running, after countDown
在程序里使用Pthreads库有一个额外的好处:分析工具认得它们,懂得其语意。线程分析工具如Intel Thread Checker和Valgrind-Helgrind等能识别Pthreads调用,并依据happens-before关系分析程序有无data race.
最后
以上就是慈祥钢笔为你收集整理的muduo库源码阅读之MutexLock、MutexLockGuard、Contidion、CountDownLatch分析的全部内容,希望文章能够帮你解决muduo库源码阅读之MutexLock、MutexLockGuard、Contidion、CountDownLatch分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复