Multithreading 这个代码需要同步吗?

Multithreading 这个代码需要同步吗?,multithreading,thread-safety,race-condition,Multithreading,Thread Safety,Race Condition,我计划在我的游戏项目中编写一个多线程部分: 线程A:从磁盘加载一堆对象,这需要几秒钟的时间。加载的每个对象递增一个计数器 线程B:一个游戏循环,在该循环中,我要么显示加载对象数量的加载屏幕,要么在加载完成后开始操作对象 在代码中,我相信它将如下所示: Counter = 0; Objects; THREAD A: for (i = 0; i < ObjectsToLoad; ++i) { Objects.push(LoadObject()); ++

我计划在我的游戏项目中编写一个多线程部分:

线程A:从磁盘加载一堆对象,这需要几秒钟的时间。加载的每个对象递增一个计数器

线程B:一个游戏循环,在该循环中,我要么显示加载对象数量的加载屏幕,要么在加载完成后开始操作对象

在代码中,我相信它将如下所示:

Counter = 0;
Objects;

THREAD A:
    for (i = 0; i < ObjectsToLoad; ++i) {
        Objects.push(LoadObject());
        ++Counter;
    }
    return;

THREAD B:
    ...
    while (true) {
        ...
        C = Counter;
        if (C < ObjectsToLoad)
            RenderLoadscreen(C);
        else
            WorkWithObjects(Objects)
        ...
    }
    ...
计数器=0;
物体;
线程A:
对于(i=0;i
从技术上讲,这可以算作竞争条件-对象可能已加载,但计数器尚未递增,因此B读取旧值。我还需要将计数器缓存在B中,这样它的值在检查和渲染之间不会改变

现在的问题是-我应该在这里实现任何同步机制,比如使计数器原子化或引入一些互斥或条件变量吗?这里的要点是,我可以安全地牺牲一次循环迭代,直到计数器改变。从我得到的情况来看,只要A只写值,而B只检查值,一切都很好


我一直在和一个朋友讨论这个问题,但我们无法达成一致,所以我们决定征求在多线程方面更有能力的人的意见。语言是C++,如果有帮助。

种族条件通常只有当两个线程尝试<强>非原子读修改时同时写入同一个数据< /强>。在这种情况下,只有一个线程写入(线程A),而另一个线程读取(线程B)

正如您所说,您将遇到的唯一“不正确”是,如果对象已加载,但计数器未递增。这会导致B读取陈旧数据,因为加载和增量操作不是以原子方式执行的

如果你不介意这种无辜的反常现象,那么它就可以正常工作


如果这使你恼火,那么你需要一次执行所有的<强>加载和增量<强>语句(通过使用锁或任何其他同步原语)。

你必须考虑内存可见性/缓存。如果没有内存屏障,这很可能会导致数秒的延迟,直到数据对线程B(1)可见为止

这适用于两种类型的数据:
计数器
对象
列表

C++11标准(2)保证只有在不引入竞争条件的情况下,多线程程序才能正确执行。没有同步,程序基本上有未定义的行为(3)。然而,在实践中,它可能会在没有

可以,使用互斥锁并同步访问
计数器
对象


(1) 这是因为每个CPU核心都有自己的寄存器和缓存。如果您不告诉CPU
核心A
其他
核心B
可能对数据感兴趣,它可以通过将数据保留在寄存器中等方式进行优化<代码>核心A
必须将数据写入更高级别的内存区域(二级/三级缓存或RAM),以便
核心B
可以加载更改

(2) C++11之前的任何版本都不关心多线程。通过第三方库支持互斥体、原子等,但该语言本身是线程无关的。
见:

(3) 问题是,您的代码可以在不同的阶段重新排序(以便更有效地执行):在编译器、汇编程序和CPU。您必须通过原子或互斥体添加内存屏障来告诉计算机哪些指令需要保持该顺序。这在大多数语言中都是一样的

我建议观看这些关于C++11内存模型的非常有趣的视频:



IMO:如果您确定多线程访问的数据,请使用同步。多线程错误很难跟踪和复制,所以最好避免它们一起出现。

你还必须考虑内存可见性/缓存。你能详细介绍一下C++中的这个主题吗?从快速的谷歌搜索中,它看起来像是一些疯狂的令人兴奋的东西,在一些连理智都无法做到的地方,它悄悄地取代了UB。另外,我不能使用
volatile
来禁止这种重新排序吗?我会看看是否能找到我心目中的参考资料。不过,整个主题与语言无关。关于
volatile
:@Artalus:Edited。还要注意,C++中的代码>易失性< /代码>意味着与java不同。