两个线程在没有同步的情况下能够同时写入同一位置吗?

24 浏览
0 Comments

两个线程在没有同步的情况下能够同时写入同一位置吗?

多个线程能够同时安全地向同一个变量写入相同的值吗?\n以一个具体的例子为例——根据C++标准,下面的代码能够在每个符合规范的系统上编译、运行而不产生未定义的行为,并打印出\"true\"吗?\n

#include 
#include 
int main()
{
    bool x = false;
    std::thread one{[&]{ x = true; }};
    std::thread two{[&]{ x = true; }};
    one.join();
    two.join();
    std::printf(x ? "true" : "false");
}

\n这是一个理论问题;我想知道它是否绝对总是有效,而不是在实践中是否有效(或者写这样的代码是否是一个好主意:))。如果有人能够指出相关的标准部分,我将不胜感激。根据我的经验,在实践中它总是有效的,但不知道它是否有保证的效果,我总是使用`std::atomic`代替——我想知道对于这种特定情况是否严格需要这样做。

0
0 Comments

在多核或多处理器的机器上,如果没有内存屏障(原子操作、互斥锁等),可能会遇到缓存一致性问题。这意味着,在两个线程都将x设置为true的情况下,即使编译器没有将x存储到寄存器中,主线程可能仍然会打印出false。这是因为主线程使用的硬件缓存尚未更新,将x从其所在的缓存行中无效。C++提供的原子类型和锁保护(以及无数的操作系统原语)是为了解决这个问题。

解决方法是使用原子类型或锁来进行同步。在多核或多处理器的环境下,这些同步机制可以确保在一个线程对共享变量进行写操作之后,其他线程可以立即看到这个写操作所做的改变。

为了解决缓存一致性问题,可以搜索"Cache Coherence Problem"和"Cache Coherence Multicore"。此外,还可以查找特定架构实现的原子事务的实现方式,比如"Intel LOCK prefix"。

需要注意的是,我的回答不仅仅涉及标准,还涉及到这些规则的实际影响。未定义行为不仅仅是一种理论上的问题:所产生的后果是真实存在的,其他低于此水平的问题通常可以被视为无关紧要。但如果你设法避开了未定义行为,了解架构在这些情况下可能会让你失误的其他方式也是很有意义的。

另一个回答似乎在暗示即使你消除了一个线程对x的写操作(从而消除了未定义行为),主线程仍然可能无法看到写操作的结果。但我不认为这是正确的:线程的完成与对应的std::thread::join的返回是同步的,这意味着在join调用返回后,主线程应该能够观察到来自写线程的所有写操作。

具体来说,主线程可能不会立即看到x值的更改,可能需要额外的时钟周期。但你可能是正确的,join调用是一个内存屏障,可以有效地同步主线程。

这个问题中包含了完整的程序;没有"yet another thread"(另一个线程)。确实如此,但是对我来说不清楚你的评论是特定于该示例还是一般情况。

0
0 Comments

是的,对于任何可以被多个线程同时更新的变量,我们都应该使用锁来确保只有一个线程在同一时间写入,从而实现一致性。

当多个线程同时写入同一位置时,会出现数据竞争问题。数据竞争是指当一个线程对内存位置进行修改时,另一个线程同时读取或修改同一内存位置。根据C++标准,任何数据竞争都会导致未定义行为。

为了解决这个问题,我们需要使用互斥锁(mutex)或者将变量声明为原子类型(atomic)。互斥锁可以确保在任意时刻只有一个线程能够访问被保护的变量,从而避免了数据竞争。原子类型的操作是原子的,可以在没有互斥锁的情况下进行线程安全的操作。

下面是一段示例代码,展示了如何使用互斥锁来保护共享变量的写操作:

#include 
#include 
#include 
std::mutex mtx;
int sharedVariable = 0;
void incrementVariable() {
    std::lock_guard lock(mtx);
    sharedVariable++;
}
int main() {
    std::thread t1(incrementVariable);
    std::thread t2(incrementVariable);
    t1.join();
    t2.join();
    std::cout << "Shared variable value: " << sharedVariable << std::endl;
    return 0;
}

在上面的代码中,我们使用了互斥锁`std::mutex`来保护`sharedVariable`的写操作。通过`std::lock_guard`类,我们可以在进入`incrementVariable`函数时自动获得锁,并在离开函数时释放锁。这样可以确保在任意时刻只有一个线程能够修改`sharedVariable`,从而避免了数据竞争。

总结起来,为了避免多个线程同时写入同一位置造成的数据竞争问题,我们需要使用锁或原子操作来保护共享变量的写操作,以确保线程安全和一致性。

0