C++ 在线程之间共享数据时,如何从不变性中获益?

C++ 在线程之间共享数据时,如何从不变性中获益?,c++,concurrency,immutability,C++,Concurrency,Immutability,我听说使用不可变数据类型可以使并发编程更安全。(例如,我正在C++中编码,试图获得这些好处。但我正在努力理解这个概念 如果我像这样创建一个不可变的数据类型: struct Immutable { public: const int x; Immutable(const int x) : x(x) {} } 我在一个线程上构造它,如何在另一个线程上使用它;i、 e.我可以做到: std::shared_ptr<Immutable> sharedMem

我听说使用不可变数据类型可以使并发编程更安全。(例如,我正在C++中编码,试图获得这些好处。但我正在努力理解这个概念

如果我像这样创建一个不可变的数据类型:

struct Immutable
{
public:
    const int x;

    Immutable(const int x)
    : x(x)
    {}
}
我在一个线程上构造它,如何在另一个线程上使用它;i、 e.我可以做到:

std::shared_ptr<Immutable> sharedMemory;

// Thread 1:
sharedMemory = std::make_shared<Immutable>(1);

// Thread 2:
DoSomething(*sharedMemory);
std::shared\ptr sharedMemory;
//线程1:
sharedMemory=std::make_shared(1);
//线程2:
DoSomething(*共享内存);
但我仍然需要使用锁定或某种屏障来保证代码的线程安全,因为当我尝试在线程2上访问sharedMemory所指向的值时,它可能无法完全构造


如何在线程之间复制不可变数据,以使并发更安全,正如不可变性所应做的那样?

只有当您有多个线程且其中至少有一个线程将写入变量时,才需要对变量进行同步。对于不可变对象,您无法对其进行写入。这意味着您可以从中读取任意数量的线程,而不会产生不良影响,因为数据永远不会更改

因此,在这种情况下,您可以静态初始化对象,在C++11及更高版本中是线程安全的,或者在线程启动之前初始化它,然后与它们共享它

// Thread 1:
sharedMemory = std::make_shared<Immutable>(1);

// Thread 2:
DoSomething(*sharedMemory);
这不是完全不变的共享状态


这是对不可变共享状态的另一种不纯使用:

cow_ptr<Document> ptr = GetCurrentDocument();

std::future<error_code> print = print_document_async(ptr);
std::future<error_code> backup = backup_document_async(ptr);

ptr.write().name = "new name";
cow_ptr ptr=GetCurrentDocument();
std::future print=打印文件异步(ptr);
std::future backup=备份\文件\异步(ptr);
ptr.write().name=“新名称”;
cow_ptr
是一个写时复制指针。它允许只读不可变访问

如果要更改它,可以调用
.write()
方法。如果您是唯一拥有该共享资源的人,那么它只会为您提供写访问权限。否则,它将克隆资源并保证其唯一性,然后为您提供写访问权限

两个不同的线程,
print
backup
线程可以访问
ptr
。他们不能更改其他线程可以看到的任何数据(允许他们编辑数据,但这只会修改数据的本地副本)

回到主线程,我们将文档重命名为新名称。打印线程和备份线程都不会看到这一点,因为它们有一个不可变(逻辑)副本

两个线程都访问相同的
ptr
变量是不合法的,但它们可以访问该
ptr
变量的副本

如果文档本身是由
cow_ptr
s构建的,则文档的“副本”只会复制内部
cow_ptr
s;也就是说,它会增加一些引用计数,而不是整个状态

修改深层元素将涉及面包屑;您需要一个
breadcrumb\u ptr
来跟踪到达给定
cow\u ptr
所需的路线。然后,它上面的
.write()
将继续将所有内容复制回“文档”的根目录,可能会在运行时替换每个指针(使用
.write()
调用)

在这个系统中,我们能够在线程之间以O(1)的代价共享非常大和复杂的数据结构shapeshot,唯一的同步开销是引用计数


这仍然不是纯粹的不变性。但在实践中,这种不纯形式的不变性带来了许多好处,并允许您高效、安全地完成一些极其危险或昂贵的事情。

我认为,这个想法是在启动多线程之前设置共享对象。
cow_ptr<Document> ptr = GetCurrentDocument();

std::future<error_code> print = print_document_async(ptr);
std::future<error_code> backup = backup_document_async(ptr);

ptr.write().name = "new name";