C++ 原子能替代品<;变体>;在C++;

C++ 原子能替代品<;变体>;在C++;,c++,atomic,variant,C++,Atomic,Variant,我的C++17代码使用的是std::atomic,定义为 using MyVariant = std::variant<std::monostate, bool, unsigned int, int, double, void*> 请告诉我您对如何1)在不关心类型的情况下对64位数据执行原子操作,以及2)稍后在不知道类型的情况下将其转换回MyVariant的想法。是否有办法内省变体的类型并以某种方式保存/使用它 如果这是不可能的,我将不得不更改我的API。使用Igor Tandet

我的C++17代码使用的是
std::atomic
,定义为

using MyVariant = std::variant<std::monostate, bool, unsigned int, int, double, void*>
请告诉我您对如何1)在不关心类型的情况下对64位数据执行原子操作,以及2)稍后在不知道类型的情况下将其转换回MyVariant的想法。是否有办法内省变体的类型并以某种方式保存/使用它


如果这是不可能的,我将不得不更改我的API。

使用Igor Tandetnik和KamilCuk提供的有用输入,这里有一个适合我需要的解决方案。这很麻烦,因为我需要维护一个并行联合定义以及通过API公开的变体,但我愿意这样做,这似乎可行,并允许我使用原子操作

感谢您的反馈

// run the code with something like: clang++ -std=c++17 main.cpp -o main && ./main


#include <atomic>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <variant>

using MyVariant = std::variant<bool, unsigned int, int, double, void*>;

int main(int argc, char const* argv[]) {

    union MyVariantAtomicUnion {
        std::atomic<bool>         bool_v;
        std::atomic<unsigned int> unsigned_v;
        std::atomic<int>          int_v;
        std::atomic<double>       double_v;
        std::atomic<void*>        void_ptr_v;
        bool                      invalid;
    };

    MyVariantAtomicUnion atomic_test_union;

    struct MyVisitor {
        MyVariantAtomicUnion& atomic_test_union_internal;

        MyVisitor(MyVariantAtomicUnion& u) : atomic_test_union_internal{u} {}

        void operator()(int int_var_val) {
            atomic_test_union_internal.int_v.store(int_var_val);
            printf("int value used\n");
        }
        void operator()(double d_var_val) {
            atomic_test_union_internal.double_v.store(d_var_val);
            printf("double value used\n");
        }
        void operator()(bool bool_v) {
            atomic_test_union_internal.bool_v.store(bool_v);
            printf("bool value used\n");
        }
        void operator()(unsigned int val) {
            atomic_test_union_internal.unsigned_v.store(val);
            printf("unsigned  value used\n");
        }
        void operator()(void* val) {
            atomic_test_union_internal.void_ptr_v.store(val);
            printf("void* value used\n");
        }
    };

    MyVariant d_val = 42.22222;

    MyVisitor my_vis{atomic_test_union};
    std::visit(my_vis, d_val);
    printf("After visitation, value is %lf\n",
           atomic_test_union.double_v.load());

    d_val = 55;
    std::visit(my_vis, d_val);
    printf("After visitation, value is %d\n", atomic_test_union.int_v.load());

    d_val = true;
    std::visit(my_vis, d_val);
    printf("After visitation, bool value is %d\n",
           atomic_test_union.bool_v.load());

    d_val = reinterpret_cast<void*>(0xfeedbeef);
    std::visit(my_vis, d_val);
    printf("After visitation, value is %p\n",
           atomic_test_union.void_ptr_v.load());

    return EXIT_SUCCESS;
}
//使用如下命令运行代码:clang++-std=c++17 main.cpp-o main&。/main
#包括
#包括
#包括
#包括
#包括
使用MyVariant=std::variant;
int main(int argc,char const*argv[]{
联合粘液瘤切除术{
std::原子波;
std::原子无符号_v;
std::原子int_v;
std::原子双_v;
std::原子空洞;
布尔无效;
};
原子检验联合会;
结构MyVisitor{
MyVariantAtomicUnion&atomic\u test\u union\u internal;
MyVisitor(MyVariantAtomicUnion&u):原子测试联合内部
void运算符(){
原子测试联合内部存储(int var val);
printf(“使用的int值”);
}
void运算符()(双d_var_val){
原子测试联合内部双v存储(d变量);
printf(“使用的双值”);
}
void运算符()(bool bool_v){
原子测试联合内部存储库(bool_v);
printf(“使用的布尔值”);
}
void运算符()(无符号int val){
原子测试联合内部未签名存储(val);
printf(“使用了无符号值\n”);
}
void运算符()(void*val){
原子测试联合内部无效存储(val);
printf(“使用的无效*值”);
}
};
MyVariant d_val=42.22222;
我的访客my_vis{atomic_test_union};
std::访问(我的vis,d_val);
printf(“访问后,值为%lf\n”,
原子测试联合。双v.负载();
d_val=55;
std::访问(我的vis,d_val);
printf(“访问后,值为%d\n”,atomic_test_union.int_v.load());
d_val=真;
std::访问(我的vis,d_val);
printf(“访问后,布尔值为%d\n”,
原子测试联合.bool_v.load();
d_val=重新解释铸造(0x0);
std::访问(我的vis,d_val);
printf(“访问后,值为%p\n”,
原子测试联合.void\ptr\u v.load();
返回退出成功;
}

我敢打赌,即使使用
std::atomic
编译的编译器,
也是无锁的()
返回false。最后,一个类类型包含一个
变量
和一个
互斥锁
来保护它。您可以自己编写这样一个类。您假设
sizeof(MyVariant所持有的东西)是的,我控制着MyVariant的定义,并且知道它将符合sizeof(uint64)的要求。太好了--我将看看
std::visit
是否为我做了这件事,并将报告back@IgorTandetnik知道了。因此,对于某种通用数据容器,没有无锁方法?您建议只使用互斥锁?我可能会问,为什么不使用MyVariant=std::variant首先,这是一个很好的观点。对于几乎所有的用例,变体只由一个线程使用,我宁愿不为此支付原子开销。API还假定非原子数据类型。但是对于内部更新,我应该以同样的方式使用原子学的并行变体。我想我还必须以类似的方式使用std::visit?是吗?在你的问题中,你需要弄清楚并举例说明你到底想做什么。对我来说,这还不清楚。在被访问的类中,您可以只使用单个函数模板
模板void操作符()(std::atomic atomic_v){做您想做的;}
。我正在实现一个多线程缩减管道。API允许用户传入缩减器lambda以及该lambda的初始值,该值可以是MyVariant中的任何类型。用户的应用程序值不需要是原子的,但在reducer中,更新是通过多个线程进行的。我最初的解决方案是使用atomic,它允许无锁更新(但在某些情况下无法编译/工作)。因此,我基本上需要将非原子值复制到一个原子值中,该原子值可以用于约化,然后再次返回一个非原子值。
将非原子值复制到一个原子值。
如果这样做,首先使用任何原子值都是毫无意义的。非原子副本之间将只存在一场竞赛。
// run the code with something like: clang++ -std=c++17 main.cpp -o main && ./main


#include <atomic>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <variant>

using MyVariant = std::variant<bool, unsigned int, int, double, void*>;

int main(int argc, char const* argv[]) {

    union MyVariantAtomicUnion {
        std::atomic<bool>         bool_v;
        std::atomic<unsigned int> unsigned_v;
        std::atomic<int>          int_v;
        std::atomic<double>       double_v;
        std::atomic<void*>        void_ptr_v;
        bool                      invalid;
    };

    MyVariantAtomicUnion atomic_test_union;

    struct MyVisitor {
        MyVariantAtomicUnion& atomic_test_union_internal;

        MyVisitor(MyVariantAtomicUnion& u) : atomic_test_union_internal{u} {}

        void operator()(int int_var_val) {
            atomic_test_union_internal.int_v.store(int_var_val);
            printf("int value used\n");
        }
        void operator()(double d_var_val) {
            atomic_test_union_internal.double_v.store(d_var_val);
            printf("double value used\n");
        }
        void operator()(bool bool_v) {
            atomic_test_union_internal.bool_v.store(bool_v);
            printf("bool value used\n");
        }
        void operator()(unsigned int val) {
            atomic_test_union_internal.unsigned_v.store(val);
            printf("unsigned  value used\n");
        }
        void operator()(void* val) {
            atomic_test_union_internal.void_ptr_v.store(val);
            printf("void* value used\n");
        }
    };

    MyVariant d_val = 42.22222;

    MyVisitor my_vis{atomic_test_union};
    std::visit(my_vis, d_val);
    printf("After visitation, value is %lf\n",
           atomic_test_union.double_v.load());

    d_val = 55;
    std::visit(my_vis, d_val);
    printf("After visitation, value is %d\n", atomic_test_union.int_v.load());

    d_val = true;
    std::visit(my_vis, d_val);
    printf("After visitation, bool value is %d\n",
           atomic_test_union.bool_v.load());

    d_val = reinterpret_cast<void*>(0xfeedbeef);
    std::visit(my_vis, d_val);
    printf("After visitation, value is %p\n",
           atomic_test_union.void_ptr_v.load());

    return EXIT_SUCCESS;
}