muduo网络库源码复现笔记(二):base库的Atomic.h

Muduo网络库简介

muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。
muduo网络库的核心代码只有数千行,在网络编程技术学习的进阶阶段,muduo是一个非常值得学习的开源库。目前我也是刚刚开始学习这个网络库的源码,希望将这个学习过程记录下来。这个网络库的源码已经发布在GitHub上,可以点击这里阅读。目前Github上这份源码已经被作者用c++11重写,我学习的版本是没有使用c++11版本的。不过二者大同小异,核心思想是没有变化的。我的代码在这里。如果你对我之前的博客有兴趣,可以点击下面的连接:
muduo网络库源码复现笔记(一):base库的Timestamp.h

Atomic.h

类成员

Atomic.h头文件顾名思义,是封装了原子操作,目的是在使用中统计消息数目。Atomic的私有成员只有一个value_。注意修饰词volatile说明编译器每次使用value_时必须从内存里重新读取,不能使用寄存器中的备份。

private:
  volatile T value_;

而它的公有成员均是对这个变量进行原子性操作的成员函数,下面讲述一下原子操作的重要性。

原子操作的重要性

而这个头文件所封装的原子操作,它的含义是不能被线程调度机制打断的操作。线程调度机制是由cpu确定的,如果没有合理的操作,将会带来很多麻烦。举例来说,假设定义一个变量x=100,然后使用两个线程对它依次进行加1操作。按照我们的想法,则是线程一操作结束x=101,线程2操作结束,x=102…这样依次增长,然而在实际中,可能并不会这样工作,让我们分析一下为什么。
当我们对x=10进行加1操作的时候,cpu做了这么三件事情:(1)将x=10从内存中取出,存在cpu的寄存器当中;(2)在寄存器中对x加一,x=11;(3)将x的新值写回内存。
因此如果我们使用两个线程对x进行加1操作时,可能会出现这样的情况:
(1)在线程1中将x从内存中取出,存在cpu的寄存器当中,线程1停止工作,切换至线程2,在线程2中将x从内存中取出,存在cpu另外一个寄存器当中,线程2停止工作,切换至线程1;
(2)在线程1中,寄存器1中对x加一,x=11,线程1停止工作,切换至线程2,寄存器2中对x加一,x=11,线程2停止工作,切换至线程1;
(3)在线程1中,将寄存器1中x的新值写回内存,x=11,线程1停止工作,切换至线程2,在线程1中,将寄存器1中x的新值写回内存,x=11。
在这种情况下,线程一和二结束工作后x的值是11,而不是我们想要的12。原子操作的重要性不言而喻。

无锁的原子操作

通过上面的分析,我们明白不对线程操作做约束是不行的。就对x加一这个问题来说,约束的方法就是在每个线程对x操作时,强制它必须完成加一的三个操作(读取修改写会)才能切换线程。为了实现这样的操作,有很多办法,比如熟悉操作系统编程的同学会使用互斥量mutex,也就是锁。这是一个常见的方法,但是在服务器端,这样的方法往往会因为锁的争夺限制服务器性能,所以要考虑无锁的原子操作。muduo中采用的方法是使用__sync_*系列的函数,这个系列的函数不使用系统提供的锁,而是直接利用cpu提供的指令,实现互斥操作,当无法获得锁时,不需要进行任务调度,从而减轻了任务切换而引起的系统开销,下面介绍几个常用的函数。

__sync_val_compare_and_swap

函数原型

type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)

参数说明:

type *ptr:进行操作的数的地址
type oldval:用于比较的值
type:newval:将写入的值

这个函数提供原子的比较和交换,如果ptr == oldval,就将newval写入ptr,否则不写入。

__sync_lock_test_and_set

函数原型

type  __sync_lock_test_and_set (type *ptr, type value, ...)

参数说明:

type *ptr:进行操作的数的地址
type valuel:用于比较的值
返回值:操作前的值

这个函数将ptr设为value并返回ptr操作之前的值。

__sync_fetch_and_add

函数原型

type __sync_fetch_and_add (type *ptr, type value);

参数说明:

type *ptr:进行操作的数的地址
type valuel:用于相加的值
返回值:操作前的值

应用

前面介绍了几个无锁的原子操作函数,这几个函数在Atomic.h文件中被封装成了对value_操作的函数,如:

T getAndAdd(T x){
    return __sync_fetch_and_add(&value_, x);
  }

实现了value_的加操作。其余成员函数与getAndAdd类似,不再赘述了。

你可能感兴趣的:(muduo,c++,网络,linux,web服务器)