muduo库MutexLock、MutexLockGuard、Contidion、CountDownLatch分析

  • MutexLock
  • MutexLockGuard
  • Condition
  • 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::UnassignGuard ug(mutex_);//这里先给mutex_的holder_置零。在等其析构时会给holder_赋值
    MCHECK(pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()));//给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_加锁,然后返回,这两步也是原子操作。

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

#include <muduo/base/Mutex.h>
#include <muduo/base/Thread.h>
#include <muduo/base/Condition.h>
#include <boost/bind.hpp>

#include <stdio.h>  

using namespace  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()后,子线程才运行。

#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=%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, t2
    printf("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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-30 01:37:19

muduo库MutexLock、MutexLockGuard、Contidion、CountDownLatch分析的相关文章

浅析muduo库中的线程设施

muduo是目前我在学习过程中遇到的最具有学习意义的网络库,下文将分析muduo库中的基础设施--Thread和ThreadPool. 首先,介绍在多线程编程中不可缺少的同步措施--Mutex和Condition. Mutex ``` /Mutex.h/ class MutexLock : boost::noncopyable { public: MutexLock() : holder_(0) { MCHECK(pthread_mutex_init(&mutex_, NULL));//MCHE

muduo库中TcpServer一次完整的工作流程

模拟单线程情况下muduo库的工作情况 muduo的源代码对于一个初学者来说还是有一些复杂的,其中有很多的回调函数以及交叉的组件,下面我将追踪一次TCP连接过程中发生的事情,不会出现用户态的源码,都是库内部的运行机制.下文笔者将描述一次连接发生的过程,将Channel到加入到loop循环为止. 监听套接字加入loop循环的完整过程 首先创建一个TcpServer对象,在的创建过程中,首先new出来自己的核心组件(Acceptor,loop,connectionMap,threadPool)之后T

muduo库Timestamp、Date、TimeZone分析

Timestamp类 Date类 TimeZone类 先了解关于时间的一个结构体和获取时间的一个函数. #include <time.h> struct timeval { __time_t tv_sec; /* Seconds. */ __suseconds_t tv_usec; /* Microseconds. */ }; tv_sec指从Epoc到现在的秒数. tv_usec指从Epoch到现在的微秒数. Epoch指的是一个特定的时间:1970-01-01 00:00:00 UTC.

muduo库源码剖析(一) reactor模式

一. Reactor模式简介Reactor释义"反应堆",是一种事件驱动机制.和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为"回调函数". 二. moduo库Reactor模式的实现 muduo主要通过3个类来实现Reactor模式:EventLoop,Channe

muduo库安装

一.简介 Muduo(木铎)是基于 Reactor 模式的网络库. 二.安装 从github库下载源码安装:https://github.com/chenshuo/muduo muduo依赖了很多的库,所以在安装muduo之前应该安装这些库,包括:curl.c-ares.protobuf. 前面两个在centos6.2可以使用yum安装,protobuf只能源码安装了,参见这篇文章. yum install libcurl-devel yum install c-ares-devel 在mudu

Android应用程序框架层和系统运行库层日志系统源代码分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6598703 在开发Android应用程序时,少不了使用Log来监控和调试程序的执行.在上一篇文章Android日志系统驱动程序Logger源代码分析中,我们分析了驱动程序Logger的源代码,在前面的文章浅谈Android系统开发中Log的使用一文,我们也简单介绍在应用程序中使Log的方法,在这篇文章中,我们将详细介绍Android应用程序框架

mysql慢查询日志进行按库切割重写文件然后分析

需求: 把每天的慢查询日志进行按库切割 对每个库的慢查询日志进行分析 思路: 工具/功能 一般统计信息 高级统计信息 脚本 优势 mysqldumpslow 支持 不支持 perl mysql官方自带 mysqlsla 支持 支持 perl 功能强大,数据报表齐全,定制化能力强. mysql-explain-slow-log 支持 不支持 perl 无 mysql-log-filter 支持 部分支持 python or php 不失功能的前提下,保持输出简洁 myprofi 支持 不支持 ph

Ubuntu下C++基于eigen库SVD矩阵奇异值分解效率分析

在优化求解问题中,经常要用到矩阵奇异值的SVD分解. 奇异值分解 (singularvalue decomposition,SVD)是一种可靠地正交矩阵分解法,它比QR分解法要花上近十倍的计算时间. 使用SVD分解法的用途是解最小平方误差法和数据压缩. 在Ubuntu下基于eigen C++库测试了eigen SVD算法的性能,即SVD求解最小二乘/伪逆,代码如下: //compile: g++ test_svd.cpp #include <iostream> #include <Eig

muduo::TimerId、Timer、TimerQueue分析

Linux时间函数介绍 linux中用以获取当前时间的的函数有 定时函数 timerfd介绍 TimerId介绍 Timer TimerQueue Linux时间函数介绍 linux中用以获取当前时间的的函数有: time(2) / time_t(秒) ftime(3) / struct timeb(毫秒) gettimeofday(2) / struct timeval(微秒) clock_gettime(2) / struct timespec(微秒) 还有gmtime / localtim