muduo库MutexLock、MutexLockGuard、Contidion、CountDownLatch

MutexLock

互斥量是线程同步常用的变量,但在实际中一般都是使用封装的函数,这样便于操作。其类图如下:

共有两个变量,mutex_是互斥量,holder_是用来表示给互斥量上锁线程的tid。 在构造函数中初始化互斥量mutex_和holder_(0),在析构函数中给销毁mutex_。对我接口根据名字很容易看出用法。

bool isLockByThisThread()是用来检查是否是当前线程给这个MutexLock对象加锁的,原理为比较holder_和 CurrentThread::tid()的值。

assingnHolder和unassignHolder分别在上锁时给holder_赋值,解锁是给holder_置零。assignHolder在上锁后调用,而unassignHolder在解锁前调用。

pthread_mutex_t* getPthreadMutex()可以返回指向类对象中互斥量的指针,在类外对互斥量操作,这个主要用在条件变量中。

在MutexLock中,还有一个类UnassignGuard,这个类中有一个MutexLock对象的引用,在其构造函数调用unassignHolder,析构函数中assignHolder,这个是为条件变量pthread_cond_wait()调用时设计的。在调用pthread_cond_wait()会解锁MuteLock,等待条件(其他线程会给MutexLock上锁)。

MutexLockGuard

在使用mutex时,有时会忘记给mutex解锁,为了防止这种情况发生,常常使用RAII手法。MutexLockGuard就是为此设计的,源码非常简单:

class MutexLockGuard : boost::noncopyable{ public: explicit MutexLockGuard(MutexLock& mutex): mutex_(mutex) {mutex_.lock(); } ~MutexLockGuard() {mutex_.unlock(); } private: MutexLock& mutex_;};

内部有一个MutexLock变量的引用,在构造函数初始化,并上锁;在析构函数解锁。

Condition

Condition类封装了条件变量,给出了几个接口,其源码很简单:

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));//给mutex_解锁 } // returns true if time out, false otherwise. bool waitForSeconds(int seconds); void notify() {MCHECK(pthread_cond_signal(&pcond_)); } void notifyAll() {MCHECK(pthread_cond_broadcast(&pcond_)); } private: MutexLock& mutex_; pthread_cond_t pcond_;};

在这里可以看到MutexLock::UnassignGuard的应用。 pthread_cond_wait(&pcond_, mutex_.getPthreadMutex())会先给mutex_解锁,然后等待条件。这两步是原子操作。在条件成立后,它会给mutex_加锁,然后返回,这两步也是原子操作。

在学了上面三个类后,可以使用封装的库来写生产者消费者模型了。生产者消费者模型可以参考。

muduo;struct msg{struct msg *next;int num; }; struct msg *head; MutexLock mutex;Condition con(mutex);void consumer()//消费者 {struct msg *mp;for(;;){{MutexLockGuard lock(mutex);while(head==NULL)//无货的话,等待生产者生产 con.wait();mp=head;head=mp->next;}printf(“Consume %d\n”,mp->num);free(mp);sleep(rand()%5);} } void producer()//生产者 {struct msg *mp;for(;;){mp=static_cast<msg*> (malloc(sizeof(struct msg)));mp->num=rand()%1000+1;printf(“Produce %d\n”,mp->num);{MutexLockGuard lock(mutex);mp->next=head;head=mp;}con.notify();sleep(rand()%5);} } int main(int argc, char **argv) {Thread t1(boost::bind(producer), “Producer”);Thread t2(boost::bind(consumer), “Consumer”);t1.start();t2.start();t1.join();t2.join();return 0; }CountDownLatch

这个是参考Java的一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

例如一组线程等待一个命令,,让命令到来时,这些线程才开始运行。或者一个线程等待多个线程运行结束后才可以运行。 其源码很简单:

class CountDownLatch : boost::noncopyable{ public: explicit CountDownLatch(int count); void wait(); void countDown(); int getCount() const; private: mutable MutexLock mutex_; Condition condition_; int count_;};

先看私有变量,count_为一个数值,当这个数值为零时,才会通知阻塞在调用wait()的线程。函数void countDown()每次调用都会给count_减1。

以一个例子说明,主线程创建了2个子线程,这两个子线程调用的函数阻塞在wait()上。当主线程调用countDown()后,子线程才运行。

using namespace muduo;CountDownLatch latch_(1);void Function(){latch_.wait();//wait for latch_ countDownprintf(“Thread ID=%d, Name=%s\n”, 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 countDown\n”);latch_.countDown();sleep(3);//wait for thread t1, t2printf(“main thread running, after countDown\n”);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

人生难免有挫折,但你是逃避不了的,一定要去面对它

muduo库MutexLock、MutexLockGuard、Contidion、CountDownLatch

相关文章:

你感兴趣的文章:

标签云: