muduo网络库剖析——线程Thread类

news/2024/7/10 20:47:40 标签: 网络, c++, 服务器, 后端, 个人开发, linux, 开源

muduo网络库剖析——线程Thread类

  • 前情
    • 从muduo到my_muduo
  • 概要
  • 框架与细节
    • 成员
    • 函数
    • 使用方法
  • 源码
  • 结尾

前情

从muduo到my_muduo

作为一个宏大的、功能健全的muduo库,考虑的肯定是众多情况是否可以高效满足;而作为学习者,我们需要抽取其中的精华进行简要实现,这要求我们足够了解muduo库。

做项目 = 模仿 + 修改,不要担心自己学了也不会写怎么办,重要的是积累,学到了这些方法,如果下次在遇到通用需求的时候你能够回想起之前的解决方法就够了。送上一段话!

在这里插入图片描述

概要

Thread类实际上是对应的one loop one thread中的thread,那么相应的thread也有自己的一些成员与函数。

框架与细节

成员

在这里插入图片描述
首先thread也有判断是否已经启动的状态位started,以及是否和父进程分离开来的状态位。以及用RAII机制来给thread进行资源管理。因为允许thread被多个对象指,所以对应的是共享指针而不是独享指针。接着是thread的线程号,这个是用来判断one loop one thread的。还有线程执行函数func_,线程的名字,有多少个线程被创建了。这个值不是单个线程独享的,所以定义为static静态变量,为了确保线程安全性,使用原子数据类型atomic_int。

在这里插入图片描述
对于atomic_int的初始化,首先肯定是在类外进行初始化的。但是这里因为atomic_int的拷贝构造函数默认是delete的,所以我们只能用构造函数的方式去初始化而不能用拷贝构造函数的方式去初始化。因此是numCreated_(0),而不是numCreated_ = 0.

函数

在这里插入图片描述
接下来是,Thread的构造函数实现。传参肯定需要线程执行函数回调,以及线程名字。并且调用设置默认名字函数。func_使用右值赋值。

在这里插入图片描述
在设置默认名字函数内,我们同时进行对线程创建数目的增加统计,如果传入的线程名字为空,就给默认的线程名字,否则就是传入的线程名。

在这里插入图片描述
接下来是线程类的析构函数,首先得先确认状态,如果是开始态,并且线程没有被分离,则线程需要分离,就调用thread自带的分离函数进行分离。

在这里插入图片描述
首先将thread启动状态位置成true。既然要启动,肯定需要让一个新线程启动起来。对与指向thread的共享智能指针,给它赋值的方式是new一个堆上的空间。因此使用shared_ptr< thread >(new thread());来创建新线程,并将指针赋给thread_。对于thread,他的创建方式这里采用c++11中的匿名函数。引用捕获变量,在匿名函数体内,我们需要记录此时的线程号。这里的好处类的作用域能够进入到新线程里,所以tid_不需要加类域。可以学习一下这种写法。除此以外,我们还要在线程中执行线程的执行回调函数。另外一个重点是,为了在start函数结束之后,tid已经是确定值,我们使用一个线程同步的方式来保证指令执行的顺序。所以我们使用了一个sem信号量。信号量的初始值是0,这样必须要等到信号量post之后才能wait到信号量。对于sem_init,第二个参数是询问是否需要用到sem给到的默认初始值,选择false就需要初始化第三个参数。

在这里插入图片描述

线程的分离也需要单独创建函数,其中将线程的状态位置1,并调用thread自带的join分离函数即可。

在这里插入图片描述
另外,定义一个CurrentThread的命名空间,其中实现了查询现在的线程的id是多少,封装在里面的是一个系统调用,大家可以学习一下。

使用方法

源码

//Thread.h
#pragma once

#include <sys/syscall.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <memory>
#include <thread>

namespace CurrentThread {
    int t_cachedTid = 0;
    inline int tid() {
        if (t_cachedTid == 0) {
            t_cachedTid = static_cast<pid_t>(::syscall(SYS_gettid));
        }
        return t_cachedTid;
    }
};

class Thread : noncopyable
{
public:
    using ThreadFunc = std::function<void()>;

    explicit Thread(ThreadFunc, const std::string &name = std::string());
    ~Thread();

    void start();
    void join();

    bool started() const { return started_; }
    pid_t tid() const { return tid_; }
    const std::string& name() const { return name_; }

    static int numCreated() { return numCreated_; }
private:
    void setDefaultName();

    bool started_;
    bool joined_;
    std::shared_ptr<std::thread> thread_;
    pid_t tid_;
    ThreadFunc func_;
    std::string name_;
    static std::atomic_int numCreated_;
};

//Thread.cc
#include <semaphore.h>

#include "Thread.h"

std::atomic_int Thread::numCreated_(0);

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

Thread::~Thread() {
    if (started_ && !joined_)
    {
        thread_->detach(); // thread类提供的设置分离线程的方法
    }
}

// 一个Thread对象,记录的就是一个新线程的详细信息 
void Thread::start() {
    started_ = true;
    sem_t sem;
    sem_init(&sem, false, 0);

    // 开启线程
    thread_ = std::shared_ptr<std::thread>(new std::thread([&](){
        // 获取线程的tid值
        tid_ = CurrentThread::tid();
        sem_post(&sem);
        // 开启一个新线程,专门执行该线程函数
        func_(); 
    }));

    // 这里必须等待获取上面新创建的线程的tid值
    sem_wait(&sem);
}

void Thread::join() {
    joined_ = true;
    thread_->join();
}

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

结尾

以上就是线程Thread类的相关介绍,以及我在进行项目重写的时候遇到的一些问题,和我自己的一些心得体会。发现写博客真的会记录好多你的成长,而且对于一个好的项目,写博客也是证明你确实有过深度思考,并且在之后面试或者工作时遇到同样的问题能够进行复盘的一种有效的手段。所以,希望uu们也可以像我一样,养成写博客的习惯,逐渐脱离菜鸡队列,向大佬前进!!!加油!!!

也希望我能够完成muduo网络库项目的深度学习与重写,并在功能上能够拓展。也希望在完成这个博客系列之后,能够引导想要学习muduo网络库源码的人,更好地探索这篇美丽繁华的土壤。致敬chenshuo大神!!!

鉴于博主只是一名平平无奇的大三学生,没什么项目经验,所以可能很多东西有所疏漏,如果有大神发现了,还劳烦您在评论区留言,我会努力尝试解决问题!


http://www.niftyadmin.cn/n/5340621.html

相关文章

【C++】用wxWidgets实现多文档窗体程序

一、基本步骤和示例代码 在wxWidgets中&#xff0c;要实现多文档窗体程序&#xff0c;通常会使用wxMDIParentFrame和wxMDIChildFrame类来创建一种标准的MDI&#xff08;多文档接口&#xff09;应用。以下是基本步骤和示例代码&#xff0c;演示如何使用wxWidgets创建多文档界面…

云计算入门——Linux 命令行入门

云计算入门——Linux 命令行入门 前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 介绍 如今&#xff0c;我们许多人都熟悉计算机&#xff08;台式机和笔记本电…

面试题(三)mybatis

1、动态sql标签 if标签(逻辑判断)where标签(SQL判断)choose&#xff0c;when&#xff0c;otherwise(Java中的switch)set (sql修改)trim(截断 添加)bind&#xff08;模糊查询&#xff09;foreach(循环)sql (复用) 2、mybatis是如何实现缓存的&#xff1f;什么是一级缓存&#…

JVM系列-4.类加载器

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

JUC并发编程-常用的多线程操作辅助类(必会)、读写锁、阻塞队列

8. 常用的辅助类(必会) 1&#xff09;CountDownLatch CountDownLatch: 减法计数器 CountDownLatch是一个同步辅助类&#xff0c;在多线程环境中用于控制线程的执行顺序。它可以让一个或多个线程等待其他线程完成一组操作后再继续执行。 CountDownLatch通过一个计数器来实现&…

pytorch学习笔记(十一)

优化器学习 把搭建好的模型拿来训练&#xff0c;得到最优的参数。 import torch.optim import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear from torch.utils.data import DataLoaderdataset torchvision.datas…

NLP自然语言处理原理应用讲解

自然语言处理&#xff08;NLP&#xff09;是人工智能领域中研究如何让计算机理解和处理人类自然语言的一门学科。它的应用广泛&#xff0c;例如在搜索引擎、聊天机器人、机器翻译等领域中都发挥了重要的作用。 NLP的基本原理是通过对大量的语料库进行训练&#xff0c;让计算机…

【漏洞复现】CloudPanel makefile接口远程命令执行漏洞(CVE-2023-35885)

文章目录 前言声明一、CloudPanel 简介二、漏洞描述三、影响版本四、漏洞复现五、修复建议 前言 CloudPanel 是一个基于 Web 的控制面板或管理界面&#xff0c;旨在简化云托管环境的管理。它提供了一个集中式平台&#xff0c;用于管理云基础架构的各个方面&#xff0c;包括 &a…