【问题标题】:Do shared variables between threads always require protection ?线程之间的共享变量是否总是需要保护?
【发布时间】:2014-08-07 19:38:21
【问题描述】:

假设我有两个线程读取和修改 bool / int“状态”。处理器保证读取和写入是原子的。

Thread 1:

if (state == ENABLED)
{
    Process_Data()
}

Thread 2:

state = DISABLED

在这种情况下,是的,线程 1 可以读取状态并进入 Process_Data 的“如果”,然后线程 2 可以更改状态。但在这一点上仍然继续处理 Process_Data 并没有错。是的,如果我们窥视引擎盖,我们会发现状态不一致,我们进入了 Process_Data 函数。但是在下一次执行 Thread1 后,它会得到 state = DISABLED 而不是 Process_Data。

我的问题是我是否仍然需要在这两个线程中加锁才能使 Thread1 的检查状态和进程原子化和 Thread2 的写入原子化(写入线程 1)?

【问题讨论】:

  • 作为一般规则,从来没有*您会总是需要某些东西的情况。 (*从不,作为另一个绝对,通常是错误的。)这真的取决于你的用例。正如您已经说过的,当state 实际上是DISABLED 时,您不介意运行Process_Data,因此您不需要锁。当然,这是假设 Process_Data 不会与线程 2 上发生的任何事情发生冲突。
  • "Atomic by the processor" 只是意味着“不会发生撕裂的读/写”。这与内存可见性无关(阅读缓存一致性)。
  • state = DISABLED,没有任何编译器或硬件障碍,可能会导致线程 1 永远不会看到线程 2 执行state = DISABLED 的效果

标签: multithreading thread-safety race-condition


【解决方案1】:

您已经解决了原子性问题。但是,在现代处理器中,您不仅要担心原子性,还要担心内存可见性。

例如,线程 1 在一个处理器上执行,并从 state 读取 ENABLED - 从其处理器的缓存中。

同时,线程 2 正在另一个处理器上执行,并将 DISABLED 写入其处理器缓存上的 state

如果没有更多代码 - 例如,在某些语言中,声明 state volatile - DISABLED 值可能很长时间不会刷新到主内存。如果线程 2 最终将值更改回 ENABLED,它可能永远不会刷新到主内存。

同时,即使 DISABLED 值被刷新到主内存,线程 1 也可能永远不会拾取它,而是无限期地继续使用其缓存值 ENABLED。

通常,如果您想在线程之间共享值,最好使用适合您正在使用的编程语言和环境的适当机制来明确执行此操作。

【讨论】:

    【解决方案2】:

    无法笼统地回答您的问题。如果您使用的语言、编译器、线程库和/或平台的规范表明您需要保护,那么您就需要保护。如果它说你没有,那么你没有。我相信每个线程库或多线程实现都指定了合理使用和共享数据的规则。如果你没有,那是一块无法可靠使用的垃圾,你应该买一个更好的。

    不要误以为“这是安全的,因为我想不出任何可能出错的方法。”或者“我对此进行了测试,我无法让它失败,所以它是安全的。”这种想法会产生脆弱的代码,当您更改编译器选项、升级 CPU 或在不同平台上运行程序时,这些代码往往会失败。请遵循您使用的工具的规范。

    【讨论】:

    • 也就是说,在我看来,仅仅因为它“更安全”而增加保护是错误的。我的问题旨在确定如果没有发生数据损坏,实例的内部状态不一致是否可以接受。
    • 哪个平台或线程库说您可以有内部状态不一致但没有数据损坏?我见过的大多数要么完全指定行为,要么将其记录为未定义/未指定。在大多数情况下,这种中间立场纯属虚构。
    猜你喜欢
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 2015-06-17
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多