2

アトミックポインターがあるとします:

std::atomic<void*> mItems;

そして、あるスレッドがそれにアクセスする必要がある関数では、最初にそれをチェックし、それがnullの場合、スレッドはそれにメモリを割り当てます:

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 個のスレッドがこのメソッドの同時実行を実行し、mItemsnull に等しいと判断した場合、それらすべてがメモリを割り当て、そのうちの N - 1 個が agian を解放します。

より良いアプローチで同様のメソッドを作成するにはどうすればよいですか。

4

2 に答える 2

1

他のスレッドが既に割り当てを行っていることを示すフラグとして、よく知られている値 (たとえば、グローバルのアドレス) を使用して、ポインターをミューテックスにすることができると思います。

したがって、値は次のとおりです。NULL->魔法の「進行中の割り当て」ポインター->実際の割り当て。

コードは次のようになります。

  • ロード アドレス: 次のいずれかの値になります。
    1. NULL: 魔法の値を持つ CAS
      • CASは成功しましたか?はいの場合、私たちは割り当てを行っており、誰もがそれを知っています
        • 割り当てを行い、新しいアドレスを保存します。これで完了です (最初の CAS で除外が保証されているため、CAS を実行する必要はありません)。
      • いいえ、他の誰かが割り当てを行っている場合は、1 に戻ります
    2. アドレスは NULL ではなく、マジック値
      • そのため、誰かがすでに割り当てを行っています-変更されるまで待って、最終的な値を使用してください
    3. NULLでも魔法でもないので、すでに実際に割り当てられた値です-そのまま使用してください

この方法では、1 つのスレッドだけが割り当てを行いますが、他の 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 に答える