learn_muduo

线程属性

  • 线程标识 pthreadId_,pid_t
  • 线程函数 func_
  • 线程名字 name_
  • 线程序号 numCreated_
bool             started_;       // 线程状态标识
bool            joined_;
pthread_t       pthreadId_;     // pthread_函数使用
pid_t           tid_;           // 线程标识
ThreadFunc      func_;          // 线程函数
string          name_;          // 线程名字
CountDownLatch  latch_;         // 倒计时
static AtomicInt32 numCreated_; // 线程序号

pthread_t的值很大,无法作为一些容器的key值。 glibc的Pthreads实现实际上把pthread_t作为一个结构体指针,指向一块动态分配的内存,但是这块内存是可以反复使用的,也就是说很容易造成pthread_t的重复。也就是说pthreads只能保证同一进程内,同一时刻的各个线程不同;不能保证同一个进程全程时段每个线程具有不同的id,不能保证线程id的唯一性。

在LINUX系统中,建议使用gettid()系统调用的返回值作为线程id,这么做的原因:
返回值是一个pid_t,其值是一个很小的整数,方便输出。
在linux系统中,它直接标识内核任务调度id,可通过/proc文件系统中找到对应项:/proc/tid 或者 /proc/pid/task/tid,方便定位到具体线程。任何时刻都是唯一的,并且由于linux分配新的pid采用递增轮回办法,短时间内启动多个线程也会具有不同的id。

执行过程

new Thread

通过new Thread(&threadFunc)初始化创建Thread

Thread::Thread(ThreadFunc func, const string& n): started_(false),joined_(false),pthreadId_(0),tid_(0),func_(std::move(func)),name_(n),latch_(1)
{setDefaultName();
}

在Thread的构造函数中,初始化线程属性。同时调用setDefaultName()设置线程的名字name_

void Thread::setDefaultName()
{int num = numCreated_.incrementAndGet();if (name_.empty()) {char buf[32];snprintf(buf, sizeof buf, "Thread%d", num);name_ = buf;}
}

start

void Thread::start()
{assert(!started_);started_ = true;detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);if (pthread_create(&pthreadId_, NULL, &detail::startThread, data)){started_ = false;delete data;// LOG_SYSFATAL << "Failed in pthread_create";} else {latch_.wait();assert(tid_ > 0);}
}

start()在堆上创建ThreadData对象,作为线程函数的参数,然后通过pthread_create()创建并启动线程。

ThreadData包含了线程的基本属性.

typedef muduo::Thread::ThreadFunc ThreadFunc;
ThreadFunc func_;
string name_;
pid_t* tid_;
CountDownLatch* latch_;ThreadData(ThreadFunc func,const string& name,pid_t* tid,CountDownLatch* latch): func_(std::move(func)),name_(name),tid_(tid),latch_(latch){}

当pthread_creat()创建成功,接着是倒计时类latch_.wait(),初始的 count_ = 1,主线程阻塞到mutex_上等待线程的结束。

void CountDownLatch::wait()
{MutexLockGuard lock(mutex_);while (count_ > 0) {condition_.wait();}
}
void wait()
{MutexLock::UnassignGuard ug(mutex_);int ret = pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()); // 将线程添加到条件变量assert(ret == 0);
}

runInThread

线程函数的执行是通过ThreadData中的runInThread调用func_()完成的。

void runInThread()
{*tid_ = muduo::CurrentThread::tid();tid_ = NULL;latch_->countDown(); // 倒计时减1latch_ = NULL;muduo::CurrentThread::t_threadName = name_.empty() ? "muduoThread" : name_.c_str();::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName);try{func_(); // 执行线程函数muduo::CurrentThread::t_threadName = "finished";} catch (const Exception& ex) {muduo::CurrentThread::t_threadName = "crashed";fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());fprintf(stderr, "reason: %s\n", ex.what());fprintf(stderr, "stack trace: %s\n", ex.stackTrace());abort();} catch (const std::exception& ex) {muduo::CurrentThread::t_threadName = "crashed";fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());fprintf(stderr, "reason: %s\n", ex.what());} catch (...) {muduo::CurrentThread::t_threadName = "crashed";fprintf(stderr, "unknow exception caught in Thread %s\n", name_.c_str());throw;}
}

当子线程开始执行,倒计时的latch_.count_ == 0,就会调用condition.notrifyAll()唤醒阻塞的主线程。

void notifyAll()
{int ret = pthread_cond_broadcast(&pcond_);assert(ret == 0);
}

测试程序

依次使用1到8个线程,每个线程向vector中push1千万个数,测试他们需要的时间

#include "muduo/base/Timestamp.h"
#include "muduo/base/Mutex.h"
#include "muduo/base/Thread.h"
#include "muduo/base/CountDownLatch.h"#include <iostream>
#include <stdio.h>
#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <vector>muduo::MutexLock g_mutex; // 声明锁
std::vector<int> g_vec;
const int kCount = 10000000; // 每次插入1000w个数void threadFunc()
{for (int i = 0; i < kCount; ++i) {muduo::MutexLockGuard lock(g_mutex); // 上锁g_vec.push_back(i);}
}void test_Mutex()
{std::cout << "---- Mutex ----\n";const int kMaxThreads = 8; // 最多8个线程g_vec.reserve(kMaxThreads * kCount); // 提前分配大小muduo::Timestamp start(muduo::Timestamp::now()); // 当前时间戳// 单个线程不用锁的时间for (int i = 0; i < kCount; ++i) {g_vec.push_back(i);}printf("1 thread(s) without lock %f\n", muduo::timeDifference(muduo::Timestamp::now(), start));for (int i = 0; i < kMaxThreads; ++i) {// i个线程用锁的时间boost::ptr_vector<muduo::Thread> threads;g_vec.clear();start = muduo::Timestamp::now(); // 更新时间戳for (int j = 0; j <= i; ++j) {threads.push_back(new muduo::Thread(&threadFunc)); // 创建线程threads.back().start(); // 启动线程  }for (int j = 0; j <= i; ++j) {threads[j].join(); // 回收线程}printf("%d thread(s) without lock %f\n", i+1, muduo::timeDifference(muduo::Timestamp::now(), start));}
}int main() {test_Mutex();return 0;
}/*
---- Mutex ----
1 thread(s) without lock 0.390272
1 thread(s) with lock 1.485214
2 thread(s) with lock 10.677879
3 thread(s) with lock 11.748183
4 thread(s) with lock 16.022083
5 thread(s) with lock 19.676071
6 thread(s) with lock 23.740399
7 thread(s) with lock 27.879850
8 thread(s) with lock 32.507374
*/

muduo学习笔记 线程类相关推荐

  1. muduo学习笔记 日志类

    learn_muduo Logger Logger有六个日志等级 TRACE DEBUG INFO WARN ERROR FATAL 日志的输出语句是通过宏定义完成,编译期完成宏定义替换,创建Logg ...

  2. muduo学习笔记-Acceptor类

    Acceptor类一般由TCPServer创建,负责处理客户端发送的connect,它拥有一个acceptSocket_和acceptChannel_成员. 1.创建Acceptor : TcpSer ...

  3. 多线程编程学习笔记——线程池(二)

    接上文 多线程编程学习笔记--线程池(一) 三.线程池与并行度 此示例是学习如何应用线程池实现大量的操作,及与创建大量线程进行工作的区别. 1. 代码如下 using System; using Sy ...

  4. muduo学习笔记:net部分之Http--HttpServer

    前面[muduo学习笔记:net部分之Http–HttpRequest.HttpResponse 和 HttpContext]介绍了TCP数据数据Buffer承载的HTTP报文的解析,本文结合TcpS ...

  5. ASM学习笔记2 - 类的创建和修改 —— ClassWriter的综合应用

    ASM学习笔记2 - 类的创建和修改 -- ClassWriter的综合应用 上回我们说到,通过使用ClassVisitor和ClassReader,我们能够分析已经存在的类.这一节中,我们将使用Cl ...

  6. Python学习笔记 (类与对象)

    Python学习笔记 (类与对象) 1.类与对象 面向对象编程语言类: 一个模板, (人类)-是一个抽象的, 没有实体的对象: (eg: 张三, 李四) 属性: (表示这类东西的特征, 眼睛, 嘴巴, ...

  7. Machine Learning A-Z学习笔记12-分类模型性能评级及选择

    Machine Learning A-Z学习笔记12-分类模型性能评级及选择 1.简单原理 一般认为假阴性比假阳性更严重,如核酸检测 用混淆矩阵表示如下图 准确率驳论(Accuracy Paradox ...

  8. JAVA学习笔记(类的学习)

    JAVA学习笔记(类的学习) 类声明和类体 构造方法和对象创建 对象的引用和实体 成员变量 方法 方法重载 关键字this 包 import语句 访问权限 对象数组 反编译和文档生成器 JAR文件 1 ...

  9. python面向对象编程72讲_2020-07-22 Python学习笔记27类和面向对象编程

    一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习. 声明:本人学习是在扇贝编程通过网络学习的,相关的知识.案例来源于扇贝编程.如果使用请说明来源. 第27关 类与面向对 ...

最新文章

  1. BZOJ4292 : [PA2015]Równanie
  2. 2020百度云秀最新成绩单,AI Cloud活跃客户数同比去年增长65%
  3. MySQL 和 Innobackup 不定期卡住的秘密
  4. SHOI2014 三叉神经树
  5. springboot入门知识点(一)
  6. python 有效的字母异位词
  7. 求复变函数的 Taylor 展式与 Laurent 展式[华中师范大学2010年复变函数复试试题]...
  8. 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器,sqlserver
  9. rgss加密文件解包器_Unity AssetBundle高效加密案例分享
  10. 《大数据》第1期“研究”——大数据管理系统评测基准的挑战与研究进展(下)...
  11. 草稿 DataGridView 控件 1129
  12. python tcp实例_实现tcp的自动重连实例教程
  13. 解决VS2013+IE11调试DevExpress ASP.NET MVC的性能问题
  14. 基于机器视觉的曼秀雷敦洗发水瓶印刷缺陷检测
  15. 人脸方向学习(八):Face Recognition-MobileFaceNets解读
  16. pathinfo函数
  17. python画图颜色随机_python随机颜色
  18. 计算机软考网络工程师中级多少分过,2019年计算机软考网络工程师中级及格分数...
  19. 计算机应用基础实训教程侯冬梅,计算机应用基础实训教程
  20. idea 2020,2021,2022石皮-解到2099年怎么搞

热门文章

  1. MFC中快速应用OpenCV教程
  2. CVS代码库管理安装配置
  3. [原创 URL重写步骤
  4. MPEG原理分析及MPEG音频编码器的调试
  5. 1.1 编程语言介绍
  6. BASIC-13 数列排序
  7. 树莓派AI视觉云台——4、树莓派的登录控制方式
  8. 第十届 蓝桥杯样题 —— 5个砝码
  9. Ubuntu下安装pip
  10. [学习方法]如何解析源代码