首页 > 其他分享 >02_muduo_base1

02_muduo_base1

时间:2024-11-08 10:20:01浏览次数:3  
标签:02 muduo return void 原子 base1 线程 进程 id

5.3 At0mic源码剖析

为什么需要原子性操作:在多线程环境下,一次简单的加法操作:先从内存读取数据到寄存器,然后进行加法,最后再把数据写回内存。这是由于多线程环境下,在寄存器上的加法到写回内存这个动作不是当成一个动作执行的,而是被划分了为三个动作,导致问题。

解决方案:第一个就是上锁(lock->x++->unlock),但是这样子多个线程会导致锁竞争,这会导致并发量的下降。第二个就是原子性操作(将读取内存到寄存器,寄存器加法,写回内存)当成一个整体。

继承基类:

class AtomicIntegerT : noncopyable	//这个类是对象语义,不可拷贝

数据成员:

volatile T value_;	//加上volatile确保该指令不会被编译器优化而省略,具体来说就是每次都是重新在内存中读取数据,而不是使用在寄存器中的备份。

一些接口:

  T get(){	//获取当前value的值,并且是一个原子性操作,也就是线程安全
    return __sync_val_compare_and_swap(&value_, 0, 0);
  }
  T getAndAdd(T x){//原子性的获取value值并且加上x,主要返回的是没有修改的值
    return __sync_fetch_and_add(&value_, x);
  }
  T addAndGet(T x){//先加再获取,返回修改之后的值
    return getAndAdd(x) + x;
  }

C++11封装的原子性操作如下:

  1. store:将值写入原子对象。
  2. load:从原子对象读取值。
  3. exchange:替换原子对象的值,并返回该对象之前的值。
  4. compare_exchange_weak / compare_exchange_strong:比较原子对象的值,并在相等时替换为新值。
  5. fetch_add / fetch_sub:对原子对象进行加/减操作,并返回操作前的值。
  6. fetch_and / fetch_or / fetch_xor:对原子对象进行位与/位或/位异或操作,并返回操作前的值。
  7. ++ / --:原子地递增或递减对象的值。
  • 一个小例子

    #include <iostream>
    #include <thread>
    #include <atomic>
    const int MAX = 1000;
    std::atomic_int total(0);//定义一个原子类型的变量
    void threadTask() {
        for (int i = 0; i < MAX; i++) {
            total.fetch_add(1);//进行原子操作
        }
    }
    

    一些额为信息

5.4 Exception类源码剖析⭐

数据成员:message:异常信息字符串。stack:异常发生时候栈回溯的信息。

成员方法:what()返回message,stackTrace()返回stack。fillStackTrace()记录异常发生栈回溯的信息【注意在C++11版本中已经没有这个函数了】。

继承标准库中的exception类。远古版本中fillStackTrace()包含了两个函数。一个是backtrace(),栈回溯,保存各个栈帧的地址。还有一个是backtrace_symbols()根据地址转换为相应的函数符号。

5.5 Thread线程的封装⭐⭐⭐

类图

线程标识符

在Linux中,每一个进程有一个pid,类型是pid_t。可以通过getpid()获取。Linux下的POSIX(只能在unix-linux环境使用)线程也有一个id,类型是pthread_t,由pthread_self()获取,改id是由线程库维护,其id是各个进程独立的,也就是各个进程可能有相同的id。Linux中POSIX线程库实现的线程本质上是一个轻量级进程,这是该进程与主进程共享一些资源。

但是有的时候我们可能需要线程的真实pid,比如进程P1先进程P2发送信号时,既不能使用P2的pid,更不能使用线程的pthread id(一方面是因为POSIX维护的是各个进程中的线程,这里的线程id并不是真实的线程id,第二个方面是发送信号通常是在操作系统的进程层次处理的,POSIX尽管是一个进程有多个线程,但是本质还是在进程的环境下执行的。),而是只能使用该线程的真实pid,也就是tid。

可以使用gettid()获取tid,但是glibc并没有实现该函数。只能通过Linux的系统调用syscall()获取,也就是使用return syscall(SYS_gettid);这里会将返回的真实id存储起来,避免频繁的调用。

代码

class Thread : noncopyable{
 public:
  typedef std::function<void ()> ThreadFunc;
  explicit Thread(ThreadFunc, const string& name = string());
  // FIXME: make it movable in C++11
  ~Thread();

  void start();
  int join(); // return pthread_join()

  bool started() const { return started_; }
  // pthread_t pthreadId() const { return pthreadId_; }
  pid_t tid() const { return tid_; }
  const string& name() const { return name_; }

  static int numCreated() { return numCreated_.get(); }

 private:
  void setDefaultName();

  bool       started_; 				//线程是否启动
  bool       joined_;
  pthread_t  pthreadId_;			//线程pthreadid
  pid_t      tid_;					//线程的真实id
  ThreadFunc func_;					//线程的回调函数
  string     name_;					//线程名称
  CountDownLatch latch_;

  static AtomicInt32 numCreated_;	//已经创建的线程个数
};

额外知识

__thread关键字:gcc内置的线程局部存储设施。只能修饰POD(plain old data)类型,与C语言兼容,也就是C语言中的类型都是POD类型。对于像用户定义的构造函数或者虚函数不是POD类型,但是一个类如果没有构造函数之类的也算POD类型。一个小例子。

__thread string obj1("hello");//error,因为会调用构造函数
__thread string* obj2 = new string; //error,__thread变量初始化要求是编译时常量,而new string是一个运行时表达式。
//在fork时,也就是真正实现数据拷贝/创建子线程之前父进程会先调用prepare,创建成功之后,父进程会调用parent,子进程会调用child
int pthread_atfork(void(*prepare)(void), void(*parent)(void), (void)(*child)(void));

线程的一次创建流程

void afterFork(){
  muduo::CurrentThread::t_cachedTid = 0;
  muduo::CurrentThread::t_threadName = "main";
  CurrentThread::tid();
}
ThreadNameInitializer(){
    muduo::CurrentThread::t_threadName = "main";
    CurrentThread::tid();
    //如果fork,那么子线程会调用afterFork()函数
    pthread_atfork(NULL, NULL, &afterFork);
  }

为什么子线程会把修改tid和name。无论是在主线程还是在子线程中fork,创建出来的新进程都是继承了原有线程的部分状态,但是这个新进程和原有线程所在的线程其实是两个独立的执行实体。所以需要重新设置一下信息。

多线程fork造成死锁的场景

父进程创建一个线程,并对mutex上锁。此时父进程会把自己的内存状态等信息拷贝到子进程。但是对于线程来说,子进程只会复制调用fork的那个特定线程状态,而不是父进程中全部状态。这样子会导致原来父进程如果存在多个线程,线程A上锁,解锁。但是fork的线程是线程B,但是线程B没有上锁、解锁能力。这样子就会导致子进程死锁。注意这里互斥锁子进程也会拷贝,这里的拷贝是指将互斥锁的状态也拷贝也拷贝过去,但是由于两个进程是独立的内存空间,父进程对互斥锁的修改并不会影响到子进程。

非POD类型数据如何进行线程局部存储

线程特定数据tsd。

标签:02,muduo,return,void,原子,base1,线程,进程,id
From: https://www.cnblogs.com/wzy-cc/p/18534587

相关文章

  • 【全99集】强推!这可能是C站最全的大模型零基础全套教程,2024最新版,草履虫能都学会!存下
    ChatGPT的出现在全球掀起了AI大模型的浪潮,2023年可以被称为AI元年,AI大模型以一种野蛮的方式,闯入你我的生活之中。从问答对话到辅助编程,从图画解析到自主创作,AI所展现出来的能力,超出了多数人的预料,让不少人惊呼:“未来是属于AI的”。AI大模型——成为互联网从业者必备技能。......
  • [赛记] NOIP2024加赛1 && 多校A层冲刺NOIP2024模拟赛18
    暴力错解大赛玩游戏82pts乱糊的错解,正确性和时间复杂度都不对,但是拿了82pts;对于正解,考虑从$k$将原序列分成两个部分,左边和右边,然后分别求一次前缀和(注意这里,可以省去很多分讨和常数),设前一个前缀和数组为$a$,后一个为$b$,那么问题就转化成有两个指针$i,j$,可以任......
  • 102_api_intro_stockcn_stockcnlimitup
    A股涨停板实时数据API数据接口股票/A股/涨停数据,所有A股涨停板实时数据,A股涨停数据/实时数据。1.产品功能支持所有A股涨停板实时数据查询;包含A股实时交易多项指标数据;毫秒级查询性能;全接口支持HTTPS(TLSv1.0/v1.1/v1.2/v1.3);全面兼容AppleATS;全......
  • 【2024-11-07】琐碎是福
    20:00落水荷塘满眼枯,西风渐作北风呼。黄杨倔强尤一色,白桦优柔以半疏。门尽冷霜能醒骨,窗临残照好读书。拟约三九吟梅雪,还借自家小火炉。                                            ......
  • NOIP2022 做题笔记
    由于本人NOIP2023做的太烂了,被教练拉去做NOIP2022了qwqfirsthour:这t1看上去还行,先写了secondhour:t2看上去有些难度,让我想一想thirdhour:快想出来了,先写一写吧fourthhour:写写写写写.....最后100pts遗憾离场......赛后有了深刻的认识,很多题是不能一步到位的,只能拼暴力......
  • CSP2024游记
    CSP2024游记现在才写好像有点晚了,不过没有关系(\(Part1\)日记\(Day0\)上午一点也不想做题,看了一上午,但不知道看了什么教练给整了场\(IOI\)赛制模拟赛,但发现T1一眼不会就没打上大巴了,感觉挺高级的(没团体出游过导致的),在大巴上遇见了gzx&Merlin第一次用耳机,挺好,就是听歌......
  • 20222410 2024-2025-1 《网络与系统攻防技术》实验五实验报告
    1.实验内容总结一下本周学习内容,不要复制粘贴2.实验过程2.1查询baidu.com并获取指定信息2.1.1DNS注册人及联系方式使用whois+域名,可以获取DNS注册人及联系方式whoisbaidu.com还可以使用在线域名查询工具获取相关信息:可以查出注册人为MarkMonitor,Inc.,联系方式为abus......
  • 中文汉化 数学计算软件:MATLAB R2023a 商业数学软件下载
    MATLABR2023a是由MathWorks公司推出的一款强大的数值计算和科学编程软件,广泛应用于工程、科学和数学领域。它提供了丰富的数值计算功能,包括线性代数、数值积分、微分方程等,并支持数据可视化、算法开发、深度学习和云服务集成等功能。用户可以使用MATLAB进行高效的矩阵运算、信号......
  • 多校A层冲刺NOIP2024模拟赛19
    多校A层冲刺NOIP2024模拟赛19其实不太想写博客,但还是从今天开始坚持写吧。T1图书管理对于这种一边大一边小的问题,一般套路是大的设为$1$,小的设为$-1$,这道题也是,这样之后去扫一遍,两端的前缀和值一样即可。T2两棵树新学到的一个重要$trick$是:$$连通块的个数......
  • GB/T 713-2023 第4部分:规定低温性能的镍合金钢 08Ni3DR、07Ni5DR、06Ni7DR、06Ni9DR
    08Ni3DR、07Ni5DR、06Ni7DR、06Ni9DR承压设备用钢板和钢带 第4部分:规定低温性能的镍合金钢承压设备包括锅炉、压力容器、气瓶和压力管道,这类设备广泛用于国民经济各个方面,其共同特点是涉及生产和生命安全,一-旦发生事故危害性较大。制造承压设备的材料多种多样,钢材是实......