无锁代码中的延迟初始化
Lazy initialization in lock free code
假设我有一个原子指针:
std::atomic<void*> mItems;
在一个函数中,当一个线程需要访问它时,它首先检查它,如果它是空的,线程将为它分配内存:
void* lItems = std::atomic_load_explicit(&mItems, memory_order_relaxed);
if(lItems == nullptr)
{
void* lAllocation = malloc(...);
if(!std::atomic_compare_exchange_strong_explicit(
&mItems,
&lItems,
lAllocation,
memory_order_relaxed,
memory_order_relaxed))
{
free(lAllocation);
}
}
...
但是,如果 N 线程并发运行此方法并发并看到mItems
等于 null,则它们都将分配内存,其中 N - 1 个将再次释放。
我如何用更好的方法编写类似的方法。
我想你可以让指针成为你的互斥锁,使用一些众所周知的值(比如全局的地址(作为其他线程已经在做分配的标志。
因此,您的值为:NULL ->神奇的"分配正在进行"指针 ->实际分配。
代码将执行以下操作:
- 加载地址:它将具有以下值之一:
- 空:具有魔法值的 CAS
- 中科院成功了吗?如果是,我们正在进行分配,每个人都知道
- 进行分配,存储新地址,我们就完成了(不必对其进行 CAS,因为我们已经保证了第一个 CAS 的排除(
- 不,然后其他人正在分配,回到 1
- 中科院成功了吗?如果是,我们正在进行分配,每个人都知道
- 地址不是空,而是魔术值
- 所以有人已经在进行分配 - 只需等到它发生变化,然后使用最终值
- 既不是 NULL 也不是魔法,所以它已经是一个真正的分配值 - 只需使用它
- 空:具有魔法值的 CAS
这样,只有一个线程执行分配,但其他 N-1 线程可能正忙于等待。这是否真的更好会有所不同...
据我所知,一旦第一个线程执行您的函数,您就需要该结构,那么在启动任何线程之前移动分配怎么样?我的意思是,重新排列代码,以便使用原子指针作为参数调用您的函数,并且在生成任何调用您的函数的线程之前分配结构(如果分配失败,您还可以避免创建任何线程(
像这样:
std::atomic<void*> mItems;
void func_that_uses_mItems();
int main()
{
mItems = init_struct();
std::thread t1 { &func_that_uses_mItems };
std::thread t2 { &func_that_uses_mItems };
// ... join with or detach threads ...
return( 0 );
}
如果分配失败,则会引发异常,并且不会启动任何线程。
相关文章:
- 模板化类中静态成员的延迟初始化
- 我的子类的属性是延迟初始化的
- 在声明或实现中延迟初始化C++单一实例
- 使用 lambda 和可变参数模板延迟初始化对象 - 任意传递值
- C++11:对延迟初始化进行安全的双重检查锁定.可能的
- 本地静态对象的非延迟初始化
- 私有类成员延迟初始化的最佳做法
- 无锁代码中的延迟初始化
- 模板和延迟初始化
- 带有转发的延迟初始化
- 有没有一种方法可以延迟初始化类的静态子对象
- 堆栈对象的延迟初始化
- c++类成员和延迟初始化
- 延迟初始化缓存..我如何使它线程安全
- 延迟初始化
- 延迟初始化要初始化的对象
- 如何实现RAII +延迟初始化
- singleton的延迟初始化问题
- c++ 11中unique_ptr的延迟初始化
- std::unique_ptr的延迟初始化