muduo里的CountDownLatch类实际上是对条件变量condition进行的封装,既可以用于所有子线程等待主线程发起 “起跑” ,也可以用于主线程等待子线程初始化完毕才开始工作。

condition.h代码如下:

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//条件变量
#ifndef MUDUO_BASE_CONDITION_H
#define MUDUO_BASE_CONDITION_H #include <muduo/base/Mutex.h> #include <boost/noncopyable.hpp>
#include <pthread.h> namespace muduo
{ class Condition : boost::noncopyable
{
public:
//构造函数只能显式调用
explicit Condition(MutexLock& mutex) : mutex_(mutex)
{//构造函数初始化条件变量
pthread_cond_init(&pcond_, NULL);
}
//析构函数
~Condition()
{//析构函数销毁条件变量
pthread_cond_destroy(&pcond_);
}
//等待函数
void wait()
{
pthread_cond_wait(&pcond_, mutex_.getPthreadMutex());
} // returns true if time out, false otherwise.
bool waitForSeconds(int seconds);
//signal函数
void notify()
{
pthread_cond_signal(&pcond_);
}
//broadcast函数
void notifyAll()
{
pthread_cond_broadcast(&pcond_);
} private:
MutexLock& mutex_;//锁,不拥有他,是一个引用,不负责管理他的生存期
pthread_cond_t pcond_;//为一个条件变量
}; }
#endif // MUDUO_BASE_CONDITION_H

condition.cc

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com) #include <muduo/base/Condition.h> #include <errno.h> // returns true if time out, false otherwise.
bool muduo::Condition::waitForSeconds(int seconds)
{
struct timespec abstime;
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += seconds;
return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime);
}

CountDownLatch.h

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//对条件变量的封装,既可以用于所有子线程等待主线程发起起跑命令,
//也可以用于主线程等待子线程初始化完毕才开始工作
#ifndef MUDUO_BASE_COUNTDOWNLATCH_H
#define MUDUO_BASE_COUNTDOWNLATCH_H #include <muduo/base/Condition.h>
#include <muduo/base/Mutex.h> #include <boost/noncopyable.hpp> namespace muduo
{ class CountDownLatch : boost::noncopyable
{
public:
//构造函数,显示调用
explicit CountDownLatch(int count);
//等待函数
void wait();
//计数器减
void countDown();
//获取当前计数器的值
int getCount() const; private://mutable表明在const里可以改变他的状态
mutable MutexLock mutex_;//互斥锁
Condition condition_;//条件变量
int count_;//计数器
}; }
#endif // MUDUO_BASE_COUNTDOWNLATCH_H

CountDownLatch.cc

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com) #include <muduo/base/CountDownLatch.h> using namespace muduo;
//构造函数对值进行初始化,参数为计数器,构造mutex_对象,将mutex_对象传到condition_里面
CountDownLatch::CountDownLatch(int count): mutex_(), condition_(mutex_),count_(count)
{
} void CountDownLatch::wait()
{
MutexLockGuard lock(mutex_);
//count_不为0则一直等待
while (count_ > 0)
{
condition_.wait();
}
} void CountDownLatch::countDown()
{
MutexLockGuard lock(mutex_);
--count_;//count_减少
if (count_ == 0)
{//如果count_为0,则通知所有的等待线程
condition_.notifyAll();
}
} int CountDownLatch::getCount() const
{//得到count_的值
MutexLockGuard lock(mutex_);
return count_;
}

测试代码需要自己编写,如下所示,该程序先打印主进程id,建立3个线程,wait等待主线程发号施令,然后睡眠3秒,之后发起号令,打印各个线程的id,代码如下:

#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Thread.h> #include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <string>
#include <stdio.h>
//测试程序
using namespace muduo; class Test
{
public:
Test(int numThreads) : latch_(1),threads_(numThreads)
{//CountDownLatch对象的计数值初始化为1,线程对象数组的容量初始化为传进来的参数
for (int i = 0; i < numThreads; ++i)
{//创建numThreads个线程
char name[32];//线程的名称
snprintf(name, sizeof name, "work thread %d", i);
//创建线程,threadFunc为回调函数,因为是成员函数,所以要用&,this为当前类指针
threads_.push_back(new muduo::Thread(boost::bind(&Test::threadFunc, this), muduo::string(name)));
}
//占位符为参数
for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::start, _1));
} void run()
{//count_初始化的时候赋为1,这里只需执行一次既可以跳出等待
latch_.countDown();
} void joinAll()
{
for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::join, _1));
} private: void threadFunc()
{
latch_.wait();//等待主线程发号施令
printf("tid=%d, %s started\n", CurrentThread::tid(), CurrentThread::name()); printf("tid=%d, %s stopped\n",CurrentThread::tid(),CurrentThread::name());
} CountDownLatch latch_;//CountDownLatch对象
boost::ptr_vector<Thread> threads_;//线程对象数组
}; int main()
{//首先打印当前进程pid,当前线程tid
printf("pid=%d, tid=%d\n", ::getpid(), CurrentThread::tid());
//构造Test对象
Test t(3);
sleep(3);
printf("pid=%d, tid=%d %s running ...\n", ::getpid(), CurrentThread::tid(), CurrentThread::name());
t.run();//发号施令
t.joinAll(); printf("number of created threads %d\n", Thread::numCreated());
}

由于线程竞争,运行结果不唯一:



另一个结果:

muduo网络库源码学习————条件变量的更多相关文章

  1. muduo网络库源码学习————Timestamp.cc

    今天开始学习陈硕先生的muduo网络库,moduo网络库得到很多好评,陈硕先生自己也说核心代码不超过5000行,所以我觉得有必要拿过来好好学习下,学习的时候在源码上面添加一些自己的注释,方便日后理解, ...

  2. muduo网络库源码学习————线程池实现

    muduo库里面的线程池是固定线程池,即创建的线程池里面的线程个数是一定的,不是动态的.线程池里面一般要包含线程队列还有任务队列,外部程序将任务存放到线程池的任务队列中,线程池中的线程队列执行任务,也 ...

  3. muduo网络库源码学习————无界队列和有界队列

    muduo库里实现了两个队列模板类:无界队列为BlockingQueue.h,有界队列为BoundedBlockingQueue.h,两个测试程序实现了生产者和消费者模型.(这里以无界队列为例,有界队 ...

  4. muduo网络库源码学习————互斥锁

    muduo源码的互斥锁源码位于muduo/base,Mutex.h,进行了两个类的封装,在实际的使用中更常使用MutexLockGuard类,因为该类可以在析构函数中自动解锁,避免了某些情况忘记解锁. ...

  5. muduo网络库源码学习————线程类

    muduo库里面的线程类是使用基于对象的编程思想,源码目录为muduo/base,如下所示: 线程类头文件: // Use of this source code is governed by a B ...

  6. muduo网络库源码学习————原子性操作Atomic.h

    原子性操作可以做到比互斥锁更小的开销,在多线程编程中原子性操作是非常有用的.Atomic.h文件位于muduo/base下,代码如下: // Use of this source code is go ...

  7. muduo网络库源码学习————日志滚动

    muduo库里面的实现日志滚动有两种条件,一种是日志文件大小达到预设值,另一种是时间到达超过当天.滚动日志类的文件是LogFile.cc ,LogFile.h 代码如下: LogFile.cc #in ...

  8. muduo网络库源码学习————日志类封装

    muduo库里面的日志使方法如下 这里定义了一个宏 #define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) ...

  9. muduo网络库源码学习————线程特定数据

    muduo库线程特定数据源码文件为ThreadLocal.h //线程本地存储 // Use of this source code is governed by a BSD-style licens ...

随机推荐

  1. DevEco Toolkit使用指南--平行视界

      高效开发和创新业务是开发者一直追求的目标,当接到开发需求时,如果可以找到现成的API调用,能为开发者节省大把时间,将会留有更多的时间进行业务的创新.华为DevEcoToolkit聚合了华为丰富的开 ...

  2. 通过GSM模块发送经纬度求救信息。

    本博客作为实验笔记,仅供学习交流.(转载请注明出处) 本实验通过GSM模块:SIM900a,实现向特定手机发送sos求救信号,并且利用GPS模块:微科VK2828U7G5LF,将经纬度信息同时发送到手 ...

  3. python 字节码死磕

    前言:  如果你跟我一样,对python的字节码感兴趣,想了解python的代码在内存中到底是怎么去运行的,那么你可以继续往下看,如果你是python新手,我建议你移步它处,本文适合有点基础的pyth ...

  4. 数据结构和算法(Golang实现)(3)简单入门Golang-流程控制语句

    流程控制语句 计算机编程语言中,流程控制语句很重要,可以让机器知道什么时候做什么事,做几次.主要有条件和循环语句. Golang只有一种循环:for,只有一种判断:if,还有一种特殊的switch条件 ...

  5. 数据结构和算法(Golang实现)(27)查找算法-二叉查找树

    二叉查找树 二叉查找树,又叫二叉排序树,二叉搜索树,是一种有特定规则的二叉树,定义如下: 它是一颗二叉树,或者是空树. 左子树所有节点的值都小于它的根节点,右子树所有节点的值都大于它的根节点. 左右子 ...

  6. web中拖拽排序与java后台交互实现

    一.业务需求 1,在后台的管理界面通过排序功能直接进入排序界面 2,在排序界面能够人工的手动拖动需要排序的标题,完成对应的排序之后提交 3,在app或者是前端就有对应的排序实现了 二.页面展示 将整体 ...

  7. CORS漏洞的学习与分析

    同源策略 同源策略(Same origin policy)是一种约定,一种非常重要的安全措施,也是最基本的安全功能,它禁止了来自不同源的脚本对当前页面的读取或修改,从而限制了跨域访问甚至修改资源,防止 ...

  8. 【图解】你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗?看完图解就不愁了

    每日一句英语学习,每天进步一点点: 前言 前一篇「硬不硬你说了算!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题」得到了很多读者的认可,在此特别感谢你们的认可,大家都暖暖的. 来了,今 ...

  9. MySQL的事务隔离级别是什么?

    我是平也,这有一个专注Gopher技术成长的开源项目「go home」 背景介绍 想必事务大家都已经非常熟悉了,它是一组SQL组成的一个执行单元,要么全执行要么全不执行,这也是它的一个特性--原子性. ...

  10. VCL安装有哪几种方法?

    不是由Borland 提供的组件叫第三方组件: 就目前常见的各种形式的组件的安装方法介绍一下. 1.只有一个DCU文件的组件.DCU文件是编译好的单元文件,这样的组件是作者不想把源码公布.一般来说,作 ...