关于多线程:MPMCQueue源码分析上

31次阅读

共计 2787 个字符,预计需要花费 7 分钟才能阅读完成。

github 地址:https://github.com/rigtorp/MP…

  • 对于__cpp_lib_hardware_interference_size

这个功能测试宏示意了 c ++17 新引入的 feature:https://zh.cppreference.com/w…
相干:https://www.it1352.com/178422…
其含意是两个对象间防止假数据共享(false sharing,也被称为伪共享)的最小偏移量。
对于 false sharing : https://www.cnblogs.com/cyfon…

#ifdef __cpp_lib_hardware_interference_size
static constexpr size_t hardwareInterferenceSize = std::hardware_destructive_interference_size;
#else
static constexpr size_t hardwareInterferenceSize = 64;
#endif

下面这段代码即获取了以后硬件条件下的防止伪共享的最小偏移量,如果编译器还没有实现这个 feature,则自定义为 64 字节。(支流的硬件架构 cache line 大小为 64 字节:https://blog.csdn.net/midion9…)

为了防止伪共享的产生,让不同对象处于不同的缓存行即可,也就是设置偏移量为缓存行大小。

  • 对于__cpp_aligned_new

c++17 引入的新 feature,具体信息见: https://www.jianshu.com/p/7ce…
其作用是提供内存对齐版本的 new 运算符。

#if defined(__cpp_aligned_new)
template <typename T> using AlignedAllocator = std::allocator<T>;
#else
template <typename T> struct AlignedAllocator {
  using value_type = T;
  T *allocate(std::size_t n) {if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) {throw std::bad_array_new_length();
    }
#ifdef _WIN32
    auto *p = static_cast<T*>(_aligned_malloc(sizeof(T) * n, alignof(T)));
    if (p == nullptr) {throw std::bad_alloc();
    }
#else
    T *p;
    if (posix_memalign(reinterpret_cast<void **>(&p), alignof(T), sizeof(T) * n) != 0) {throw std::bad_alloc();
    }
#endif
    return p;
  }

  void deallocate(T *p, std::size_t) {
#ifdef WIN32
    _aligned_free(p);
#else
    free(p);
#endif
  }
};
#endif

如果编译器实现了 aligned new 的 feature,则间接实现规范库版本的内存分配器,否则
自定义一个,依据平台不同应用_aligned_malloc/posix_memalign 实现。

  • 对于 Slot 类
template <typename T> struct Slot {~Slot() noexcept {if (turn & 1) {destroy();
  }
}

template <typename... Args> void construct(Args &&... args) noexcept {static_assert(std::is_nothrow_constructible<T, Args &&...>::value, "T must be nothrow constructible with Args&&...");
  new (&storage) T(std::forward<Args>(args)...);
}

void destroy() noexcept {static_assert(std::is_nothrow_destructible<T>::value, "T must be nothrow destructible");
  reinterpret_cast<T*>(&storage)->~T();}

T &&move() noexcept { return reinterpret_cast<T &&>(storage); }

// Align to avoid false sharing between adjacent slots
alignas(hardwareInterferenceSize) std::atomic<size_t> turn = {0};
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage;
};

下面是 Slot 类的实现,重点察看以下几个中央:

  1. 数据成员 turn 的修饰符含意
  2. 数据成员 storage 的作用
  3. 析构函数
  4. construct 函数中的 placement new 操作

应用 std::aligned_storage 定义了一个大小最多为 sizeof(T), 对其要求为 alignof(T) 的对象 storage。并在 construct 函数中通过 placement new 操作在其上结构 T 类型对象。
这一系列操作的目标就是提供给用户自定义的依照任何对齐要求的内存申请与结构的形象能力,对于这套组合拳 c ++ 规范中有清晰的标注:
As with any other uninitialized storage, the objects are created using placement new and destroyed with explicit destructor calls.
参考文章:https://blog.csdn.net/ywcpig/… 和
https://en.cppreference.com/w…
应用 alignas 修饰符将数据成员 turn 的对齐要求批改为 hardwardInterferenceSize,即文章第一局部定义的常量,因为构造体的大小必须是其数据成员最大对齐数的整数倍,因而不同 Slot 对象的 storage 不可能存在于同一个 cache line 中,防止了 false sharing。
析构函数 中依据 turn 是否为 0 选择性调用 destroy 函数析构对象,能够看到 turn 的另一个作用是标记此 Slot 中是否还存在 T 类对象。
tips:
对 c ++ 语法不够纯熟的同学能够再看下这段代码,波及到大括号初始化,类内初始化,xvalue 的产生,可变参数的完满转发,noexcept 等新规范个性。

正文完
 0