muduo库的ThreadLocal类剖析

muduo源码分析:线程特定/私有数据类ThreadLocal 线程私有数据 1.__thread : gcc内置的线程局部存储设施 __thread只能修饰POD类型 POD类型(plain old data),与C兼容的原始数据,例如,结构和整型等C语言中的类型是 POD 类型,但带有用户定义的构造函数或虚函数的类则不是 __thread string t_obj1(“cppcourse”); // 错误,不能调用对象的构造函数 __thre 阅读详情


首先来看一个概念:线程特定数据

>>>在单线程程序中,我们经常用全局变量共享数据。多线程环境下,全部变量被所有线程所共有。

>>>但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效

>>>POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据称之为线程特定数据(Thread-specific Data,或TSD)

>>>对于POD类型,可以用__thread来解决。


POSIX线程库通过四个函数操作线程特定数据,分别是pthread_key_create,pthread_key_delete,pthread_getspecific,pthread_setspecific。

create创建一个key,一旦一个线程创建了一个key,那么所有的线程也都有这个key。我们可以为特定的线程指定特定的数据,可以使用set指定,get获取。那么这些数据就是每个线程所私有的,这样不同的线程的key就指向了不同的数据。delete是删除这个key,不是删除数据,删除数据要在create的时候指定一个回调函数,由回调函数来销毁数据,这个数据是堆上的数据就可以销毁。


ThreadLocal类的视图如下:



分析如下:

#ifndef MUDUO_BASE_THREADLOCAL_H
#define MUDUO_BASE_THREADLOCAL_H

#include <muduo/base/Mutex.h>  // MCHECK

#include <boost/noncopyable.hpp>
#include <pthread.h>

namespace muduo
{

template<typename T>
class ThreadLocal : boost::noncopyable
{
 public:
  ThreadLocal()
  {
    //构造函数中创建key,数据的销毁由destructor来销毁
    MCHECK(pthread_key_create(&pkey_, &ThreadLocal::destructor));  
  }

  ~ThreadLocal()
  {
    //析构函数中销毁key
    MCHECK(pthread_key_delete(pkey_));
  }

  //获取线程特定数据
  T& value()
  {
    T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_)); //通过key获取线程特定数据
    if (!perThreadValue)  //如果是空的,说明特定数据还没有创建,那么就空构造一个
    {
      T* newObj = new T();
      MCHECK(pthread_setspecific(pkey_, newObj));  //设置特定数据
      perThreadValue = newObj;   //返回
    }
    return *perThreadValue;   //返回对象引用,所以需要*
  }

 private:

  static void destructor(void *x)
  {
    T* obj = static_cast<T*>(x);
    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];   //检测是否是完全类型
    T_must_be_complete_type dummy; (void) dummy; 
    delete obj;   //如果是,我们就可以删除它了
  }

 private:
  pthread_key_t pkey_;    //key的类型是pthread_key_t类型
};

}
#endif


muduo库在key上面使用的trick:

muduo库ThreadLocal类只有一个成员pkey_,它是一个pthread_key_t类型。但是我们发现,muduo库注册的destroy()函数直接将它的入口参数x指针强制转化为T类型指针。然后直接调用delete。这是什么原因呢?

在pthread_key_create()函数中,传入了一个&ThreadLocal::destructor的成员指针,当线成局部变量销毁时,如果传入的第二个参数不为NULL,系统将调用该函数取销毁实际的数据。由于是类成员函数,隐藏了一个指针是this指针,这时候void *x,x的地址实际上就是this的地址,也就是该对象的地址。

上面我们知道,ThreadLocal类只有一个成员,就是pthread_key_t类型成员pkey_(不是指针类型),我们可以再回头看pthread_key_create()函数,我们发现它传入的第一个参数正是pkey_的地址,也就是pthread_key_t*类型。所以我们在destructor()函数中使用

 T* obj = static_cast<T*>(x);

 delete obj; 

由于pkey_是传入pthread_key_create()的第一个参数,所以它的地址就是实际数据存放的地址。而(void*)this == (void*)&pkey_,所以直接强制转化this指针为T*类型,然后以

T*类型的方式delete,就会真实释放线程局部存储的数据。

这种方法相当于使用一个写死已经分配好的地址,去存储真实数据。&pkey->数据,实际上跟普通的指针功能是一样的。不过对外可以表现为一个pthread_key_t类型,相当于起了一个键值名字,看起来好像使用键值寻找指向的数据一样。


下面有部分百度百科对这个的解释:http://baike.baidu.com/link?url=mEco9WH30o91pW57KVOB6oK8irHl5FIpG2m2gcEPrj2HyB5D4j1djgxcySdLQ-tg



测试用例如下:

#include <muduo/base/ThreadLocal.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>

#include <boost/noncopyable.hpp>
#include <stdio.h>

class Test : boost::noncopyable
{
 public:
  Test()
  {
    printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
  }

  ~Test()
  {
    printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
  }

  const muduo::string& name() const { return name_; }
  void setName(const muduo::string& n) { name_ = n; }

 private:
  muduo::string name_;
};

muduo::ThreadLocal<Test> testObj1;
muduo::ThreadLocal<Test> testObj2;

void print()
{
  printf("tid=%d, obj1 %p name=%s\n",
         muduo::CurrentThread::tid(),
         &testObj1.value(),
         testObj1.value().name().c_str());
  printf("tid=%d, obj2 %p name=%s\n",
         muduo::CurrentThread::tid(),
         &testObj2.value(),
         testObj2.value().name().c_str());
}

void threadFunc()
{
  print();
  testObj1.value().setName("changed 1");
  testObj2.value().setName("changed 42");
  print();
}

int main()
{
  testObj1.value().setName("main one");
  print();
  muduo::Thread t1(threadFunc);
  t1.start();
  t1.join();
  testObj2.value().setName("main two");
  print();

  pthread_exit(0);
}

它的输出是这样的:


我们来分析一下结果:

>>>第一部分:这是主线程中第一次执行的结果,主线程中试图利用obj1的value()方法取该线程特定数据,此时线程特定数据还没有创建,所以内部调用T()创建,然后主线程设置线程特定数据的 name为"main one",同时调动打印,由于obj2没有调用value()方法,所以他的name是空的。

>>>第二部分,子线程中没有调用value()直接打印,所以name都为空。此处我们可以看到调用了构造函数,所以构造出的新的obj1,和obj2都没有名字,它们就是线程局部变量,地址也都不同于之前的全局对象的地址。

>>>第三部分,更改了局部对象的名字。

>>>第四部分,由于muduo库pthread_key_create的实现注册了destroy函数,所以子线程终止时,不仅释放key,同时会释放数据,也就是调用test的析构函数。

>>>第五部分,主线程中先打印两个全局对象名字,它们并没有改变。并且主线程结束时也调用了析构函数。


muduo库手册 感觉不错,分享一下,它的使用手册,希望它对你有帮助 立即下载

相关推荐

VScode - 从入门到精通:高效开发环境搭建指南

本文是一份全面的VSCode高效开发环境搭建指南。从选择理由、安装配置、核心插件推荐到个性化设置与高效技巧,详细讲解了如何将这款轻量级编辑器武装成强大的开发工具。重点介绍了GitLens、Prettier、Live Server等必备插件,以及通过用户设置、代码片段和快捷键打造专属高效工作流的方法,帮助开发者从入门到精通。

weixin_27734811的博客 426

muduo库学习笔记十二:base库之threadLocal

ThreadLocal类:线程本地存储类 为什么需要这个类? 我们经常要用到"全局变量"以实现多个函数间共享数据。在单线程程序中,这样做是没有问题的,但是在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时,有些数据仅仅在某个线程需要,因此有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。在muduo库学习笔记五:base库之Thread类中我...

mazi的博客 503

Java utgard连接OPC问题记录

怎么开启是个问题, 是在引入openscada的jar包里的Sever类中修改,这就犯难了, 其实没必要, 只要在自己项目下创建与openscada中Server类相同的路径,并复制Server过去改就行了, 项目会优先使用本地类,像下面这样。奇迹发生了,0x00000005解决. 注: 我的安全补丁编码跟网上查到的并不一样, 所以最初没有删,后来全都删了就好了,这个编码规则咱也不知道.安全日志中记录用户登录行为, OPC通信需要使用Windows系统用户进行登录, 如果用户名密码错误这里会有相关记录.

wangxin1949的博客 2840

muduo之ThreadLocal

ThreadLocal用来存储线程私有数据的类。 // 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) #ifn...

1万+

从零到一:YOLOv11高帧率模型在RDK X5上的ONNX转换与部署实战

本文详细介绍了将最新的YOLOv11高帧率模型部署到地平线RDK X5开发板的完整实战流程。内容涵盖从PyTorch模型导出ONNX、地平线工具链环境配置与模型量化编译,到编写高效的板端推理代码,并针对高帧率目标进行了后处理优化。通过实战避坑指南,帮助开发者在RDK X5上实现30FPS以上的实时目标检测。

weixin_29089057的博客 139

Type-c超声波切割刀

接下来,我将提供Type-C超声波切割刀项目的C代码实现示例,代码将按照上述分层架构和模块化设计进行组织。为了达到3000行以上的代码量,我将尽可能详细地实现各个模块的功能,并添加必要的注释和错误处理。通过采用分层架构和模块化设计,我们可以建立一个可靠、高效、可扩展的系统平台,为产品的成功奠定坚实的基础。实际项目中,需要更详细的硬件驱动、更完善的功能模块、更严格的测试验证和更周全的维护升级计划。针对Type-C超声波切割刀这个项目,我将从以下几个方面详细阐述最适合的代码设计架构,并提供具体的C代码实现。

lianmengde的博客 1189

【muduo】base篇---ThreadLocal

一、线程特定数据和线程本地存储   线程本地数据(局部数据/私有数据/线程中特有的线程存储),和__thread类似,虽然__thread简单,但容易不正确使用。   线程存储:每个线程都有各自的线程存储,直到线程被销毁(或者主动调用函数销毁)。线程存储既不是全局变量(不同的线程有各自的线程存储,而且也不能相互访问)、也不是static变量(线程函数中的static变量可以被多个线程修改)也不是局...

青萍之末的博客 542

谷歌(Google) Pixel 8/9 Pro(xl)如何获取 root 权限

谷歌(Google)Pixel8/9Pro(xl)等系列手机破解后,会在你的init_boot.img同路径生成一个magisk_patched.img,把这个文件拷贝到adb目录下,稍后还得把它刷回手机,这样手机才能获得Root权限,因为安卓14、15以后的内核结构调整,和Pixel7及以下的6、5、4、3、2等系列手机破解稍微不一致。因为ROOT权限的获取,需要使用ROM中的内核文件,个人强烈建议在官网下载ROM,直接刷最新版,其他地方的ROM如果没有校验码,请自行斟酌是否使用。

bugsycrack的博客 3486

Muduo之封装ThreadLocal、ThreadLocalSingleton

基础知识: Linux线程基础知识 1、ThreadLocal ThreadLocal可以叫做Thread的局部变量,通过ThreadLocal创建的对象每个线程都拥有其副本被各自线程独有,操作其对象不会造成线程不安全。这是通过封装Linux操作系统提供的线程特定数据实现,封装也比较简单,不涉及技巧。 template&lt;typename T&gt; class ThreadLocal...

一个菜鸟的博客 963

S7-1200 PN与G120变频器控制起停及调速PROFINET实现详解

S7-1200 PLC与G120变频器通过PROFINET通信可实现高效、实时的电机控制。2. 《S7-1200与G120 CU250S-2PN的PROFINET通讯指南》- 控制器:S7-1200 PLC (如1214C DC/DC/DC)- 变频器:G120 CU250S-2PN (带PROFINET接口)- 输出区(PZD1-2):控制字(16位)、速度设定值(16位)- 输入区(PZD1-2):状态字(16位)、实际速度值(16位)- P922=1:选择报文类型为"标准报文1 (PZD2/2)"

电工 1867

Oracle EBS Interface/API(29) - AP发票API

续上编文章《Oracle EBS Interface/API(28) - 客制化开发AP付款API》背景,介绍如调用官方提供标准AP发票API调用 用户界面 Invoices(Payables Manager->Invoices->Entry->Invoices) Open Interface Invoices(Payables Manager->Invoices->Entry->Open Interface Invoices) 快速参考 参考点 内容.

陈显平的专栏-Oracle EBS 1150

【Muduo源码剖析笔记】 基础组件之线程库

Muduo库源码阅读笔记

LeeEdgar的博客 217

从零手搓大模型(一)文本如何变成 LLM 的输入

本文介绍了从原始文本到LLM输入向量的完整处理流程。主要内容包括: 文本预处理:通过正则表达式将原始文本分割成token列表,处理标点和空格。 词表构建:创建token到ID的映射字典(vocabulary),这是文本数字化的关键步骤。 简单tokenizer实现:封装了编码(encode)和解码(decode)功能,完成文本与token ID的相互转换。 处理流程:原始文本 → 分词 → token ID → 嵌入表示 → 最终输入向量。 数据形状变化:从字符串逐步转换为(batch_size, cont

echo的博客 1000

muduo_base代码剖析之线程特定数据的封装ThreadLocal

线程特定数据 (线程内私有的全局变量)来源 在单线程程序中,我们经常要用到“全局变量”以实现多个函数间能共享数据 在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程共享 但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中生效,但却可以跨多个函数访问 POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据称为Thread-specific Data / TSD 线...

JaweG 265

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

muduo网络库

muduo网络库摘要 muduo是一个专为Linux设计的高性能TCP网络编程库,采用主从Reactor模型实现高并发处理。核心架构包括: 事件驱动模型:基于epoll的事件循环机制,每个线程一个EventLoop,实现无锁高并发 分层设计:分为底层工具和网络框架两层,通过shared_ptr管理连接生命周期 核心组件: EventLoop:线程事件循环核心 Channel:文件描述符事件管理器 TcpServer/TcpClient:服务端/客户端框架 Acceptor/Connector:连接管理组件

CX33071的博客 424

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

如何高效掌握muduo:C++11多线程网络库的核心线程模型与优化实践指南

muduo是一个基于C++11的事件驱动型多线程Linux服务器网络库,它以高效的线程模型和卓越的性能表现,成为众多高性能服务器开发的理想选择。本文将深入剖析muduo的核心架构,帮助开发者快速理解其线程模型设计与优化实践。 ## 📚 muduo核心架构概览 muduo网络库采用了经典的Reactor模式,将网络事件处理与业务逻辑解耦,实现了高效的事件驱动编程。其核心模块主要分布在以下目录:

gitblog_00554的博客 904

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

muduo网络库:高性能C++网络编程实践

Reactor模式是现代网络编程中的核心架构,通过事件驱动机制实现高效IO处理。其核心原理是将IO事件注册到事件循环(EventLoop),由系统通知应用程序事件就绪,从而避免主动轮询的资源浪费。这种非阻塞IO模型特别适合开发高并发的TCP服务器应用,能够显著提升CPU利用率。在实际工程中,基于Reactor模式的网络库如muduo通过"one loop per thread"的线程模型,结合自动扩容的Buffer设计和时间轮定时器算法,为开发者提供了高性能、易用的网络编程框架。muduo网络库作为典型的C

weixin_34124651的博客 351

muduo网络库学习之ThreadLocal<T> 类、ThreadLocalSingleton<T>类封装知识点

1、在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。 2、在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。 3、但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。 4、POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据称为(Thread-specific Data,或 TSD)。

Meditation 4830

学习muduo库(8)之线程本地存储类ThreadLocal

预备知识: int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)); 第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果绑定的函数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往...

请叫我少爷的博客 260

muduo库源码学习(base)ThreadLocal

// 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_THREADLOCAL_H #define

lsaejn的专栏 277

muduo库的ThreadLocalSingleton类剖析

本文剖析muduo库的ThreadLocalSingleton类,即线程本地单例类。 先来看下它的类图,它的内部嵌套的一个deleter类。 分析如下: #ifndef MUDUO_BASE_THREADLOCALSINGLETON_H #define MUDUO_BASE_THREADLOCALSINGLETON_H #include #include

FreeeLinux's blog 1466

muduo基础库

muduo是一个高质量的Reactor网络库,采用one loop per thread + thread pool架构实现,代码简洁,逻辑清晰,是学习网络编程的很好的典范。 为什么需要网络库 使用Sockets API进行网络编程是一件简单地技术,但是高级语言的Sockets库并没有对Sockets API提供更高层次的封装,直接用它编写网络程序很容易掉到陷阱里,因此需要网络库来降低开发难度...

wk_bjut_edu_cn的博客 2950

Muduo库源码分析(9):线程特定数据

线程私有数据 这些数据不会因为多进程共享数据而影响,因为是每个线程特有的数据。 线程私有数据实现对于POD类型可以使用__thread来修饰(详解见Thread类封装) http://blog.youkuaiyun.com/jacktangj/article/details/76222350 非POD类型可以使用线程特定数据封装 线程特定数据讲解: http://www.cnblogs.com/java

jacktangj的博客 367

C++网络库探索(一)

前两天絮絮叨叨,没说一句有用的东西,今天打算分享一点有价值的东西。 开源网络库 网络库 语言 OS 特点 优点 缺点 适合干啥 备注 ACE C++ All “四库全书”,包含很多设计模式,超重 “太好了”,好到没人用的了 代码量大、使用容易出错、代码质量不高。 适合作研究学习 在面向对象、网络编程、设计模式等方面的研究具有重大意义 libevent、libe、libuv C ...

假正经Leo先生 2061

muduo Library

muduo是由陈硕(http://www.cnblogs.com/Solstice)开发的一个Linux多线程网络库,采用了很多新的Linux特性(例如eventfd、timerfd)和GCC内置函数。其主要特点为: 线程安全,支持多核多线程 不考虑可移植性,不跨平台,只支持 Linux,不支持 Windows。 // 支持Windows有时候代价太大了 在不...

WLin9174的博客 280
上一篇: muduo库的Singleton类剖析
下一篇: muduo库的ThreadLocalSingleton类剖析
FreeeLinux
博客等级 码龄11年 421粉丝 356原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值