小编LWi*_*sey的帖子

原子bool无法保护非原子计数器

我遇到了一个(基本的)自旋锁互斥锁的问题,似乎没有按预期工作.

4个线程正在递增受此互斥锁保护的非原子计数器.结果与使互斥体看起来破碎的预期结果不匹配.

示例输出:

  result: 2554230
expected: 10000000
Run Code Online (Sandbox Code Playgroud)

在我的环境中,它发生在以下条件下:

  • flag是的std::atomic<bool>,其他任何东西,如std::atomic<int>std::atomic_flag(和test_and_set)工作正常.

  • 使用gcc 6.3.1和-O3flag 在X86_64上编译

我的问题是,有什么可以解释这种行为?

#include <iostream>
#include <vector>
#include <atomic>
#include <thread>
#include <mutex>
#include <assert.h>

class my_mutex {
    std::atomic<bool> flag{false};

public:
    void lock()
    {
        while (flag.exchange(true, std::memory_order_acquire));
    }

    void unlock()
    {
        flag.store(false, std::memory_order_release);
    }
};


my_mutex mut;
static int counter = 0;


void increment(int cycles)
{
    for (int i=0; i < cycles; ++i)
    {
        std::lock_guard<my_mutex> lck(mut);

        ++counter;
    }
} …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading mutex atomic c++11

19
推荐指数
1
解决办法
719
查看次数

使用 decltype(auto) 推导出的返回类型是否正确?

decltype(auto) func(int&& n)
{
    return (n);
}
Run Code Online (Sandbox Code Playgroud)

clang 12.0.1接受此代码,但gcc 11.2.1拒绝它:

error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int' 
Run Code Online (Sandbox Code Playgroud)

gcc似乎使用 return type int &&,但带有n内括号,这是正确的吗?

神电演示

c++ language-lawyer decltype-auto

13
推荐指数
1
解决办法
297
查看次数

C++ shared_mutex实现

boost::shared_mutexstd::shared_mutex(C++ 17)可用于单个写入器,多个读取器访问.作为一项教育练习,我将一个使用自旋锁的简单实现放在一起并具有其他限制(例如公平策略),但显然不打算在实际应用中使用.

我们的想法是,如果没有线程持有锁,则互斥锁保持引用计数为零.如果> 0,则该值表示有权访问的读者数.如果为-1,则单个编写者可以访问.

这是一个没有数据竞争的正确实现(特别是使用的,最小的内存排序)吗?

#include <atomic>

class my_shared_mutex {
    std::atomic<int> refcount{0};
public:

    void lock() // write lock
    {
        int val;
        do {
            val = 0; // Can only take a write lock when refcount == 0

        } while (!refcount.compare_exchange_weak(val, -1, std::memory_order_acquire));
        // can memory_order_relaxed be used if only a single thread takes write locks ?
    }

    void unlock() // write unlock
    {
        refcount.store(0, std::memory_order_release);
    }

    void lock_shared() // read lock
    {
        int val;
        do {
            do …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading mutex c++11 stdatomic

11
推荐指数
1
解决办法
1407
查看次数

是否允许此编译器转换?

考虑这个代码,where xy是整数:

if (x)
    y = 42;
Run Code Online (Sandbox Code Playgroud)

是否允许以下​​编译器转换?

int tmp = y;
y = 42;

if (!x)
    y = tmp;
Run Code Online (Sandbox Code Playgroud)

背景:

这是来自Bjarne Stroustrup的FAQ:

// start with x==0 and y==0

if (x) y = 1;   // Thread 1 

if (y) x = 1;   // Thread 2
Run Code Online (Sandbox Code Playgroud)

常见问题解答说这是数据竞争免费; 使用xy0都不应该写入任何变量.
但如果允许转换怎么办?

c++ multithreading c++11 data-race

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

在发布序列中使用原子读 - 修改 - 写操作

比如说,我Foo在线程#1中创建了一个类型的对象,并希望能够在线程#3中访问它.
我可以尝试这样的事情:

std::atomic<int> sync{10};
Foo *fp;

// thread 1: modifies sync: 10 -> 11
fp = new Foo;
sync.store(11, std::memory_order_release);

// thread 2a: modifies sync: 11 -> 12
while (sync.load(std::memory_order_relaxed) != 11);
sync.store(12, std::memory_order_relaxed);

// thread 3
while (sync.load(std::memory_order_acquire) != 12);
fp->do_something();
Run Code Online (Sandbox Code Playgroud)
  • 线程#1中的存储/发布命令Foo,更新为11
  • 线程#2a非原子地将值sync增加到12
  • 线程#1和#3之间的同步关系仅在#3加载11时建立

Foo的同步

该场景被破坏,因为线程#3旋转直到它加载12,其可能无序到达(wrt 11)并且Foo不与12一起订购(由于线程#2a中的放松操作).
这有点违反直觉,因为修改顺序sync是10→11→12

标准说(§1.10.1-6):

原子存储 - 释放与从存储中获取其值的load-acquire同步(29.3).[注意:除了在指定的情况下,读取更高的值不一定能确保可见性,如下所述.这样的要求有时会干扰有效的实施. - 尾注]

它也在(§1.10.1-5)中说:

由原子对象M上的释放操作A引导的释放序列是M的修改顺序中的副作用的最大连续子序列,其中第一操作是A,并且每个后续操作
- 由执行A的相同线程执行. ,或
- 是原子读 - 修改 - 写操作.

现在,线程#2a被修改为使用原子读 - 修改 …

c++ multithreading atomic memory-model c++11

8
推荐指数
1
解决办法
737
查看次数

为什么std :: remove_if创建了这么多闭包?

在这个例子中,一个foo实例除了打印它是复制还是移动构造之外什么都不做.

#include <iostream>
#include <algorithm>
#include <vector>

struct foo {
    foo()=default;
    foo(foo &&) { std::cout << "move constructed\n"; }
    foo(const foo &) { std::cout << "copy constructed\n"; }
};

int main()
{
    foo x;
    std::vector<int> v; // empty

    std::remove_if(v.begin(), v.end(),
                   [x=std::move(x)](int i){ return false; });
}
Run Code Online (Sandbox Code Playgroud)

这会产生以下输出:

move constructed
copy constructed
move constructed
move constructed
copy constructed
copy constructed
Run Code Online (Sandbox Code Playgroud)

问题:

  • 为什么要std::remove_if创建这么多的闭包?
  • 即使需要多个中间实例,人们也会期望它们都是rvalues; 那么为什么有些人会复制?

编译器是gcc 8.1.1

c++ stl c++14

8
推荐指数
1
解决办法
295
查看次数

在原子多线程代码中删除容器

考虑以下代码:

struct T { std::atomic<int> a = 2; };
T* t = new T();
// Thread 1
if(t->a.fetch_sub(1,std::memory_order_relaxed) == 1)
  delete t;
// Thread 2
if(t->a.fetch_sub(1,std::memory_order_relaxed) == 1)
  delete t;
Run Code Online (Sandbox Code Playgroud)

我们确切地知道线程 1 和线程 2 之一将执行delete. 但是我们安全吗?我的意思是假设线程 1 将执行delete. 它是保证当线程1开始delete,线程2甚至不会看t

c++ multithreading atomic stdatomic

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

真实示例,其中std :: atomic :: compare_exchange与两个memory_order参数一起使用

您能否举一个真实的例子,std::atomic::compare_exchange出于某种原因使用两个memory_order参数版本(因此一个memory_order参数版本不合适)?

c++ multithreading atomic

5
推荐指数
1
解决办法
270
查看次数

在线程 A 中创建 std::thread 对象,在线程 B 中加入

如果对象在线程之间正确同步,是否允许调用join()std::thread不同线程中创建的对象?

例如:

void cleanup(std::thread t)
{
    t.join();
}

int main()
{
    std::thread t{[] { /* something */ }};

    std::thread c{cleanup, std::move(t)};
    c.join();
}
Run Code Online (Sandbox Code Playgroud)

c++ multithreading thread-safety c++11

5
推荐指数
1
解决办法
74
查看次数

C++ 的双重检查锁有什么潜在问题吗?

这是用于演示的简单代码片段。

有人告诉我,双重检查锁不正确。由于变量是非易失性的,编译器可以自由地重新排序调用或优化它们(有关详细信息,请参阅 codereview.stackexchange.com/a/266302/226000)。

但是我真的看到很多项目中确实使用了这样的代码片段。有人可以对这个问题有所了解吗?我用谷歌搜索并和我的朋友谈论它,但我仍然找不到答案。

#include <iostream>
#include <mutex>
#include <fstream>

namespace DemoLogger
{
    void InitFd()
    {
        if (!is_log_file_ready)
        {
            std::lock_guard<std::mutex> guard(log_mutex);
            if (!is_log_file_ready)
            {
                log_stream.open("sdk.log", std::ofstream::out | std::ofstream::trunc);
                is_log_file_ready = true;
            }
        }
    }


    extern static bool is_log_file_ready;
    extern static std::mutex log_mutex;
    extern static std::ofstream log_stream;
}

//cpp
namespace DemoLogger
{
    bool is_log_file_ready{false};
    std::mutex log_mutex;
    std::ofstream log_stream;
}
Run Code Online (Sandbox Code Playgroud)

更新:谢谢大家。InitFd()确实有更好的实现,但它确实只是一个简单的演示,我真正想知道的是,双重检查锁定是否存在任何潜在问题。

有关完整的代码片段,请参阅https://codereview.stackexchange.com/questions/266282/c-logger-by-template

c++ multithreading mutex locking atomic

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