使用大量原子变量来消除互斥体,会产生无法解释的后果

问题描述 投票:0回答:0

经过 5 天的努力(3 次重大重写),我在 StackOverflow 上向各位智者寻求帮助。我以前使用过原子 int(s),但没有这么广泛。

场景;我有 1..1000 多个批次,标记为 A..Z 的测试。批次设置 2,在知道批次 1 的测试结果处理后才能进行处理,依此类推。

    1)  A B C..Z
    2)  A B C..Z
    .
    .
   99)
100++)  A B C..Z

STAGE3(第三步) – 利用全局 Atomic Int 来分配本地值索引 (fetch_add),而不是“Do, while”循环,寻找下一个空闲“测试”(无需循环和寻找在每次测试中都是免费的锁)。它工作得很漂亮。

void Stage3(int ThreadsLevel)
{
    int index;
    CurrentWorkers.fetch_add(1); //Workers Entering
    index = IndexManager.fetch_add(1); //Assign Thread His Job (A..Z)
    do
    {
        //
        // BODY OF ALL THE WORK TO DO
        //

        index = IndexManager.fetch_add(1);
    } while (index < NumberOfTest);
    CurrentWorkers.fetch_sub(1); //Workers Exiting
}

STAGE2(门,)在工作即将完成时停止任何落后线程(Windows 可能借用并返回)加入,并在最终线程完成后将线程向前发送以开始下一批(自由流动的线程)完毕。我确切地知道工作何时完成,通过在入口处使用 Atomic Int (CurrentWorkers.fetch_add(1),在退出时使用 CurentWorkers.fetch_sub(1),在第 3 阶段)。

用途; IndexManager.Load() < NumberOfText to see if all assigned , just waiting to complete. If so blocks it from stage 3, and sends forward.

用途; (CurrentWorkers.Load() == 0)
重置; IndexManager.load(0)

void Stage2(int ThreadsLevel)
{
    if (ThreadsLevel == CurrentLevel.Load()) // < Less than Straggler Go Wait to do Next batches
    {
        if (IndexManager.load() < NumberOfTest) //greater send thread to wait for next batch
        {
            Stage3(ThreadsLevel);

            if (CurrentWorkers.load() == 0)
            {
                if (IndexManager.load() > 0)
                    IndexManager.store(0);
            }
        }
    }
}

**** 我演示了所有正在使用的原子变量,以防您认为这是编译器排序问题或其他问题。

第一阶段问题; 当测试开始时,线程被创建并发送到等待室(Stage1_Wait),以消除(A…Z)每次迭代的“创建”和“结束线程”的昂贵开销。它等待“TestReady.load()”。

主线程出去并获取样本(A..Z),设置原子整数(SamplesReady”)以释放线程。目前,它等待所有线程返回(ActiveThreads),然后再获取下一个批次。

void Stage1_Wait() //PROBLEM
{
    std::atomic_int testing;

    while (ProcessingTest.load())
    {
        if (TestReady.load()) 
        {
            Stage2(ActiveLevel); //ActiveLevel Global Variable passed by value

            SamplesReady.store(0);
            ActiveThreads.fetch_sub(1);
        }
    }
}

void MainThread_ProcessLevel()
{
    ActiveThreads.store(NumberOfThreads);       
    SamplesReady.store(1);
    int waiting = 1;
    while (waiting)
    {
        if (!ActiveThreads.load())
            waiting = 0;
    }
}

主线程外有 1 个单线程,工作正常。添加 2 个线程,在“锁定”之前它会获得超过 100 个测试级别。添加更多线程,它会在 20 个以内“锁定”。

定义锁定:ActiveThreads.fetch_sub(1) 不会递减至零。线程正在传递它,但“fetch_sub(1)”上没有更新。因此,MainThreadProcessing 只是在等待。它从未达到“0”被卡住了。

使用各种“memory_order_options”之后 – 我说好吧,让线程流动,并让 MainThread 检查“SamplesReady.load() == 0”。猜猜看……“SamplesReady”没有更新,但现在“ActiveTHreads”看到每个线程并且正在递减(感觉就像我被我的计算机恶作剧 - 哈哈)。

所以问题似乎是 MainThreadProcessing 和 Stage1_Wait。昨晚我对所有内容进行了彻底重写/重新设计(最终的就是你现在看到的)。它又悄悄地出现在我身上(同一区域)。有什么想法吗???

3天后我重写/重新设计了代码(3次)。我尝试了各种内存排序选项(在扫描 StackOverflow 和其他网站后),但在同一区域仍然得到相同的结果。

c++ windows multithreading thread-safety stdatomic
© www.soinside.com 2019 - 2024. All rights reserved.