小编Nat*_*dge的帖子

你能在同一对象的非重叠区域之间进行memcpy吗?

memcpyC17 关于[7.24.2.1p2]做了以下说明:

memcpy 函数将 s2 指向的对象中的 n 个字符复制到 s1 指向的对象中。如果复制发生在重叠的对象之间,则行为未定义。

常见的解释是您不能复制重叠的内存区域。但这并不完全相同,因为同一对象中可能存在不重叠的区域。

想象一下一个典型的实现,其中sizeof(unsigned int) == 4sizeof(unsigned short) == 2。为了简单起见,进一步假设没有陷阱表示,并且alignof(unsigned short) <= alignof(unsigned int)。考虑:

unsigned int x = 0xdeadbeef;
unsigned short *p = (unsigned short *)&x;
memcpy(&x, p+1, 2);
Run Code Online (Sandbox Code Playgroud)

解释#1:我在x和 之间复制x,对象x当然与自身重叠,所以我导致了 UB。

解释#2:由于<string.h>函数操作“被视为字符类型数组的对象”[7.24.1p1],因此我实际上将其视为x数组unsigned char[4],并且我只是将该数组的元素 2 和 3 复制到元素 0 和 1。这些unsigned char对象不以任何方式重叠,所以我没有造成UB。我得到了和我一样的效果unsigned char *q = (unsigned char *)&x; …

c memcpy language-lawyer

9
推荐指数
0
解决办法
233
查看次数

为什么在《C++ Concurrency in Action》中的无锁队列实现中应用这些内存顺序?

简要描述;简介:

下面的代码是《C++ Concurrency in Action 第二版》中无锁队列的实现。队列采用链表作为底层数据结构,采用分割引用计数技术实现。

compare_exchange_strong让我困惑的是,代码中有多个使用acquire内存顺序,但我不明白为什么使用这个顺序。

例如,在对对象count的成员的所有访问中node(见下文),不存在有顺序的存储操作release,但有很多有顺序compare_exchange_strong的操作acquire

有关此无锁队列的完整代码、我对代码的解释以及我的问题的详细描述,请阅读完整描述部分。


详细描述:

我正在阅读 Anthony Williams 所著的《C++ Concurrency in Action》第二版。本书介绍了如何使用分割引用计数技术来实现无锁队列。我首先根据我的理解简单解释一下代码是如何工作的,以帮助你快速阅读代码。稍后将给出该队列的完整实现。

该实现使用单链表来实现队列。队列保存了指向链表头节点和尾节点的指针,分别是代码中的head和,并指向一个虚拟节点。tailtail

当将元素推入队列时,我们需要将元素值放入 指向的虚拟节点中tail,然后在该节点后面添加一个虚拟节点,并让tail指向新的虚拟节点。

当从队列中弹出一个元素时,我们应该弹出 指向的元素head,然后设置headhead->next。当headtail指向同一个节点(即哑节点)时,队列为空。

该代码使用引用计数来管理已删除节点的生命周期。与节点相关的引用计数分为两部分,外部引用计数和内部引用计数。外部计数加上内部计数就是该节点的完整引用计数值。外部计数存储在指向节点的指针中(即counted_node_ptr在代码中),而内部计数存储在节点对象内部(即count在代码中)。

为了防止一个线程所指向的对象在解引用指针之前被另一个线程删除,外部计数器必须首先递增以确保该节点不被删除。这是通过increase_external_count()代码完成的。

当外部引用不再引用某个节点时,其中存储的外部计数必须添加到该节点的内部计数中,这是由 完成的free_external_counter()。内部计数存储在count.internal_count节点对象中。并count.external_counters表示引用该节点的外部引用的数量。由于这些外部引用各自拥有自己的外部计数,因此必须将这些计数全部考虑在内。当且仅当内部计数为0并且外部计数器的数量也为0时,该节点才能被安全删除。

对于pop其中未成功指向正在弹出的节点的引用(通常是因为另一个线程已经弹出了该节点),这些引用将被简单地丢弃,但必须减去它们的引用计数。这是由 完成的release_ref()。该函数将节点的内部计数减一。每次引用计数递减,或者内部计数和外部计数合并时,都会检查是否满足删除节点的条件。

下面是书中无锁队列的完整实现:

template<typename T> …
Run Code Online (Sandbox Code Playgroud)

c++ concurrency multithreading atomic lock-free

9
推荐指数
1
解决办法
362
查看次数

出于排序的目的,原子读-修改-写是一种操作还是两种操作?

考虑一个原子读-修改-写操作,例如x.exchange(..., std::memory_order_acq_rel)。出于对其他对象的加载和存储进行排序的目的,这是否被视为:

  1. 具有获取-释放语义的单个操作?

  2. 或者,作为一个获取加载,然后是一个释放存储,附加保证其他加载和存储x将同时观察它们或两者都不观察?

如果它是 #2,那么尽管在加载之前或存储之后不能对同一线程中的其他操作进行重新排序,但仍然存在在两者之间重新排序的可能性。

作为一个具体的例子,考虑:

std::atomic<int> x, y;

void thread_A() {
    x.exchange(1, std::memory_order_acq_rel);
    y.store(1, std::memory_order_relaxed);
}

void thread_B() {
    // These two loads cannot be reordered
    int yy = y.load(std::memory_order_acquire);
    int xx = x.load(std::memory_order_acquire);
    std::cout << xx << ", " << yy << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

可以thread_B输出0, 1吗?

如果x.exchange()换成了x.store(1, std::memory_order_release);那么thread_B肯定能输出0, 1。是否应该exchange()排除额外的隐式负载?

cppreference听起来像 #1 是这种情况并且0, 1被禁止:

具有此内存顺序的读-修改-写操作既是获取操作又是释放操作。当前线程中的任何内存读取或写入都不能在此存储之前或之后重新排序。

但是我在标准中找不到任何明确的内容来支持这一点。实际上,该标准对原子读-修改-写操作几乎没有说明,除了 N4860 …

c++ atomic memory-barriers stdatomic

8
推荐指数
2
解决办法
214
查看次数

你是否用(或其他介词)重新定位到另一个分支?

我最常听到同事说我用master重新调整了我的分支,但这是“正确”的说法吗?你是用 ref 来变基还是用 ref 来变基?这两种方式都可以接受/准确吗?

git terminology rebase

7
推荐指数
1
解决办法
1109
查看次数

ARM STLR 内存排序语义

我正在努力了解 ARM STLR 的确切语义。

根据文档,它具有发布语义。所以如果你有 STLR 商店,你会得到:

[StoreStore][LoadStore]
X=r1
Run Code Online (Sandbox Code Playgroud)

其中X是内存和r1一些寄存器。

问题是释放存储和获取加载无法提供顺序一致性:

[StoreStore][LoadStore]
X=r1
r2=Y
[LoadLoad][LoadStore]
Run Code Online (Sandbox Code Playgroud)

在上述情况下,允许重新排序 X=r1 和 r2=Y。为了使这个顺序一致,需要添加一个[StoreLoad]:

[StoreStore][LoadStore]
X=r1
[StoreLoad]
r2=Y
[LoadLoad][LoadStore]
Run Code Online (Sandbox Code Playgroud)

你通常在商店里这样做,因为装载更频繁。

在 X86 上,普通存储是发布存储,普通加载是获取加载。[StoreLoad] 可以通过 MFENCE 来实现,或者使用LOCK ADDL %(RSP),0Hotspot JVM 中的方式来实现。

当查看ARM文档时,LDAR似乎具有获取语义;所以这将是[LoadLoad][LoadStore]。

但 STLR 的语义很模糊。当我使用 memory_order_seq_cst 编译 C++ 原子时,只有一个 STLR;没有DMB。所以看来STLR比release store有更强的内存排序保证。对我来说,在栅栏层面上,STLR 相当于:

 [StoreStore][LoadStore]
 X=r1
 [StoreLoad]
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下吗?

concurrency multithreading arm atomic arm64

7
推荐指数
1
解决办法
1927
查看次数

为什么 GCC 会产生奇怪的移动堆栈指针的方式

我观察到 GCC 的 C++ 编译器生成以下汇编代码:

sub    $0xffffffffffffff80,%rsp
Run Code Online (Sandbox Code Playgroud)

这相当于

add    $0x80,%rsp
Run Code Online (Sandbox Code Playgroud)

即从堆栈中删除 128 个字节。

为什么 GCC 生成第一个子变体而不是添加变体?对我来说,添加变体似乎比利用下溢更自然。

在相当大的代码库中,这只发生一次。我没有最小的 C++ 代码示例来触发此操作。我正在使用海湾合作委员会7.5.0

c++ x86 assembly gcc stack-pointer

7
推荐指数
1
解决办法
240
查看次数

为什么 MinGW gcc 8.2.0 `-std=c11` 不支持 `timespec_get()`?

参考2011 年 4 月 12 日发布的 ISO/IEC 9899:201x 编程语言 - C的 N1570 委员会草案,应该有一个函数timespec_get()(参见 7.27.2.5)。

但当使用 编译以下代码片段时,MinGW gcc 版本 8.2.0 返回warning: implicit declaration of function 'timespec_get', 和:error: 'TIME_UTC' undeclaredgcc -std=c11 snippet.c

#include <time.h>

int main() {
   struct timespec tstart;
   timespec_get(&tstart, TIME_UTC);
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是为什么?

c iso mingw c11

6
推荐指数
1
解决办法
1322
查看次数

C++ 正则表达式错误!方括号表达式不适用于 icase 标志

// regex_replace example
#include <iostream>
#include <string>
#include <regex>
#include <iterator>

int main ()
{
  std::string INPUT = "Replace_All_Characters_With_Anything";
  std::string OUTEXP = "0";
  std::regex expression("[A-Za-z]", std::regex_constants::icase);
  std::cout << std::regex_replace(INPUT, expression, OUTEXP);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这在这里工作:http : //cpp.sh/6gb5a 这在这里工作:https : //regexr.com/5bt9d

问题似乎归结为是否使用 icase 标志。由于存在下划线,A in All、C 在 Characters、W in With 等不会被替换。错误似乎是,[]只有在非匹配之后没有出现所述字符时,才可以使用来匹配事物。

似乎对此有一个快速解决方案,如果括号后跟 {1},则它有效。

例子: [A-Za-z]{1}

编译器:Microsoft Visual Studio Community 2019 / 版本 16.7.3 / c++17

也在 c++14 中测试过,同样的不良行为

预期结果:
在此处输入图片说明

我的结果:
在此处输入图片说明

c++ regex visual-c++

6
推荐指数
1
解决办法
176
查看次数

数据待定:内部可变性还是单独的HashMap?

我有一个struct,称之为Book,比方说存储书店出售的一本书的数据。它需要在某些数据结构中的许多地方被引用(例如 with Rc),因此不能以正常方式可变地借用。然而,它有一些属性,比如它的价格,需要在初始化之后的某个时间填写,在对象已经有未完成的引用之后。

到目前为止,我可以想到两种方法来做到这一点,但它们都有缺点:

  • 内部可变性:给出Book一个字段,例如 price: RefCell<Option<i32>>which 被初始化为RefCell::new(Option::None)whenBook被初始化。稍后,当我们确定书的价格时,我们可以使用borrow_mutto set pricetoSome(10)代替,然后我们可以使用borrow它来检索它的价值。

    我的感觉是,一般来说,除非有必要,否则人们希望避免内部可变性,而且这里似乎没有那么必要。这种技术也有点尴尬,因为Option我们需要它,因为价格直到稍后才会有值(并且将其设置为0-1同时看起来不像 Rustlike),但是它需要很多地方matches 或unwraps我们可以从逻辑上确定价格已经被填写。

  • 单独的表:根本不将价格存储在里面Book,而是创建一个单独的数据结构来存储它,例如price_table: HashMap<Rc<Book>, i32>. 有一个函数,它在价格确定时创建并填充该表,然后通过引用(可变或不可变)将其传递给需要知道或更改书籍价格的每个函数。

    像我一样来自 C 背景,HashMap感觉在速度和内存方面都是不必要的开销,因为数据已经有一个自然的地方(内部Book)并且“应该”通过一个简单的指针追逐来访问。这个解决方案还意味着我必须用一个引用price_table.

这两种方法中的一种在 Rust 中通常更惯用,还是有其他方法可以避免这种困境?我确实看到了Once,但我认为这不是我想要的,因为我仍然需要在初始化时知道​​如何填写price,而我不知道。

当然,在其他应用程序中,我们可能需要一些其他类型而不是i32表示我们想要的属性,所以我希望能够处理一般情况。

hashmap rust interior-mutability

6
推荐指数
1
解决办法
80
查看次数

在没有 seq-cst 负载的情况下,seq-cst 栅栏与 acq-rel 栅栏完全相同吗?

我试图了解std::atomic_thread_fence(std::memory_order_seq_cst);栅栏的用途,以及它们与栅栏有何不同acq_rel

到目前为止,我的理解是,唯一的区别是 seq-cst 栅栏影响 seq-cst 操作的全局顺序 ( [atomics.order]/4)。并且只有在实际执行 seq-cst 加载时才能观察到所述顺序。

所以我想,如果我没有 seq-cst 负载,那么我可以用 acq-rel 栅栏替换所有 seq-cst 栅栏,而不改变行为。那是对的吗?

如果这是正确的,为什么我会看到这样的代码“使用栅栏实现 Dekker 算法”,它使用 seq-cst 栅栏,同时保持所有原子读/写宽松?这是该博客文章中的代码:

std::atomic<bool> flag0(false),flag1(false);
std::atomic<int> turn(0);

void p0()
{
    flag0.store(true,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_seq_cst);

    while (flag1.load(std::memory_order_relaxed))
    {
        if (turn.load(std::memory_order_relaxed) != 0)
        {
            flag0.store(false,std::memory_order_relaxed);
            while (turn.load(std::memory_order_relaxed) != 0)
            {
            }
            flag0.store(true,std::memory_order_relaxed);
            std::atomic_thread_fence(std::memory_order_seq_cst);
        }
    }
    std::atomic_thread_fence(std::memory_order_acquire);
 
    // critical section


    turn.store(1,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_release);
    flag0.store(false,std::memory_order_relaxed);
}

void p1()
{
    flag1.store(true,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_seq_cst);

    while (flag0.load(std::memory_order_relaxed))
    {
        if (turn.load(std::memory_order_relaxed) != 1)
        {
            flag1.store(false,std::memory_order_relaxed);
            while …
Run Code Online (Sandbox Code Playgroud)

c++ concurrency atomic language-lawyer stdatomic

6
推荐指数
1
解决办法
335
查看次数