C++ 在线程之间共享数据时,如何从不变性中获益?
我听说使用不可变数据类型可以使并发编程更安全。(例如,我正在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
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";