2

假设我有一个原子指针:

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 个将释放 agian。

我如何用更好的方法编写类似的方法。

4

2 回答 2

1

我想您可以将指针设为互斥体,使用一些众所周知的值(例如,全局地址)作为其他线程已经在进行分配的标志。

因此,您的值是:NULL -> 魔术“正在进行分配”指针 -> 实际分配。

该代码将执行以下操作:

  • 加载地址:它将具有以下值之一:
    1. NULL:具有魔法值的 CAS
      • CAS成功了吗?如果是,我们正在分配,每个人都知道
        • 进行分配,存储新地址,我们就完成了(不应该对它进行 CAS,因为我们已经保证使用第一个 CAS 排除)
      • 不,然后别人在做分配,回到1
    2. 地址不是NULL,而是魔法值
      • 所以有人已经在进行分配 - 等到它发生变化,然后使用最终值
    3. 既不是 NULL 也不是魔法,所以它已经是一个真正的分配值 - 只需使用它

这样只有一个线程进行分配,但您的其他 N-1 个线程可能正忙于等待。这是否真的更好会有所不同......

于 2013-10-08T11:32:22.327 回答
0

据我所知,只要第一个线程执行您的函数,您就需要该结构,那么在启动任何线程之前移动分配如何?我的意思是,重新排列代码,以便使用原子指针作为参数调用您的函数,并在生成任何调用您的函数的线程之前分配结构(如果分配失败,您也可以避免创建任何线程)

就像是:

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 );
}

如果分配失败,则抛出异常并且不启动线程。

于 2013-10-08T14:33:07.967 回答