【问题标题】:How do i sync variables across threads? C++如何跨线程同步变量? C++
【发布时间】:2012-07-29 17:38:47
【问题描述】:

假设我有 A) 全局变量 B) 局部变量,但另一个线程有一个指向它的指针

假设我有这个代码

thread2(&localVar);//now thread2 can modify it
localVar=0;
globalVar=0;
while(1){
    mutex.lock();
    cout << (globalVar && localVar ? "Both true" : "fail");
    mutex.unlock();
    Sleep(1000)
}

这是正确和安全的吗?我不记得了。如果是我的问题是 C++ 如何知道 localVar 和 globalVar 可能已被修改?如果你说它是因为互斥锁/解锁,那么我的问题是为什么?在调用任何函数时,C++ 是否认为变量可能已被修改并需要重新加载到寄存器中?

如果这不安全,那么是什么让它不安全? (我怀疑如果不是,那么只有 localVar 是不安全的),我该如何纠正它?

【问题讨论】:

  • 在修改变量之前,您的第二个线程是否也采用相同的互斥锁?
  • 请提供完整代码,我们不必猜测。
  • When calling any functions does C++ believe variables may have been modified and need to be reloaded into the register? 使互斥锁工作必须做的一切都已完成。如果这就是它所需要的,那么这就是它所做的。 (实际需要做的可能是特定于平台的。它可能涉及在所有互斥操作之前使寄存器内容无效,可能不会。)
  • @DavidSchwartz 我怀疑编译器会检查并为该函数/类做一些特殊的事情,因为它被称为互斥体
  • @acidzombie24:这取决于编译器。对于一些人来说,没有什么特别的需要。他们为所有外部功能执行此操作。对于某些功能,需要特定的“clobbers”。

标签: c++ multithreading


【解决方案1】:

你做得比实际情况复杂得多。您的责任是确保不会发生对变量的冲突访问

定义:对给定内存位置的两次访问冲突 iff它们可以同时发生并且至少一个是写访问

所以:

  • 两次写入同一内​​存位置 = 错误
  • 对同一内存位置的读取和写入 = 错误

为确保这种情况永远不会发生,您可以使用mutex,或者您将程序设计为只读取线程之间共享的变量。还有许多其他可能的策略。

如果我正确理解您的不完整代码:localVar 在两个线程中同时修改。 行为未定义。任何事情都有可能发生。

【讨论】:

  • 当/如果涉及多个值时,“至少一次写入访问”的标准不成立。如果你想正确地呈现“一组值”的当前状态,你需要那个互斥锁来保护它。
  • “不成立”我应该更清楚:在考虑需要作为一个整体看待的一组值时,这还不够。如果您允许不受限制的读取访问,您可能会修改一个而另一个“尚未更新”以反映当前状态 - 它们不同步。
  • @RichardSitze 对不起,我还是不明白;你能提供一个代码示例吗?
  • 示例:编写器线程设置时间、位置和速度——这些值相互绑定。您不希望阅读器线程读取时间t3、时间t2 和速度t3。因此,您的写入器线程将使用互斥锁来保护对绑定值集(时间、位置、速度)的更新,而您的读取器线程将使用相同的互斥锁来保护它对一组值的读取。
  • @RichardSitze 这个例子说明了语言约束和域约束之间的区别:语言只说必须保护每个单独的变量(时间、位置和速度)免受冲突访问(可能由 3 个不同的@ 987654326@ 变量:time_mutex、position_mutex 和 velocity_mutex),但高级约束意味着您只能为所有三个变量使用一个互斥锁。
【解决方案2】:

这样正确又安全吗?

没有。

修复:在创建第二个线程之前初始化 localVar 和 globalVar。

您已将 localVar(在本地堆栈上?local var 建议在范围内的堆栈上)暴露给另一个线程很麻烦。如果这个 localVar 在堆栈上,那么请确保不要让它超出范围。

您是否在另一个线程上使用相同的互斥锁来保护读/写访问?如果是,那么这就是朝着安全的方向发展。

这不是“C++ 是否知道 localVar 和 globalVar 可能已被修改?”的问题:C++ 不知道。对于这种情况,如前所述,一切都是关于使用互斥锁来正确保护对所有共享值的所有访问。如果两个线程都使用互斥锁,则一次只允许一个线程通过它,进入受保护的代码。任何其他试图锁定互斥锁的线程都将“停止并等待”,直到锁被释放。

因此,通过互斥锁包围您对这对值的读取,您走在了正确的轨道上。

当调用任何函数时,C++ 是否认为变量可能已被修改并需要重新加载到寄存器中?

一般来说,是的。

【讨论】:

  • 在我的示例中,localVar 和 globalVar 假定已经初始化或构造。假设其他线程是正确的。所以我专注于这个线程。在这个线程/示例中,while 循环是永远的,超出范围与线程/问题无关,所以忽略它。是的,我会的。但我不明白 C++ 如何知道在那时同步变量。另一个例子是假设我的代码允许两个变量之一过时(它可以等到下一个循环)为什么我不能做Flush() 而不是互斥锁(并且没有解锁)?它会知道 var 已经
  • 改变了吗? (假设 var 未标记为 volatile)。假设我没有互斥锁,这个线程会看到另一个线程的更新吗?如果不是,那么为什么 lock 允许它看到?这些是我问这个问题时想知道的事情的类型
  • @acidzombie24 您在 while 循环之前立即将 localVar 和 globalVar 都设置为 0 - 这看起来像 init 代码,并且没有保护。可能会或可能不会给您带来问题,但它充其量是不好的形式。
  • @acidzombie24 你会看到没有互斥锁的变化。看看我的 cmets 对好奇的人的回应。
  • @acidzombie24 您的声明“我不明白 C++ 如何知道同步变量”暗示了您可能会感到困惑的事情。 C++ 不同步变量 - 它只创建一个副本,您选择在两个并发线程之间共享它们。互斥锁是关于允许您限制对部分代码的访问 - 在这种情况下读取或写入共享变量。正确使用互斥锁允许您通过这种受限访问进行协调。
【解决方案3】:

我认为第一次获取局部变量的地址时,它会变得易失,所有读写都在内存中执行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-27
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    • 2017-02-18
    • 2011-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多