首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >轮询由C程序中的其他线程编写的变量安全吗?

轮询由C程序中的其他线程编写的变量安全吗?
EN

Stack Overflow用户
提问于 2014-01-19 13:06:12
回答 3查看 1.3K关注 0票数 8

考虑下面的C代码片段:

代码语言:javascript
运行
复制
int flag = 0;
/* Assume that the functions lock_helper, unlock_helper implement enter/leave in
 * a global mutex and thread_start_helper simply runs the function in separate
 * operating-system threads */

void worker1()
{
  /* Long-running job here */
  lock_helper();
  if (!flag)
    flag = 1;
  unlock_helper();
}

void worker2()
{
  /* Another long-running job here */
  lock_helper();
  if (!flag)
    flag = 2;
  unlock_helper();
}


int main(int argc, char **argv)
{
  thread_start_helper(&worker1);
  thread_start_helper(&worker2);
  do
  {
    /* doing something */
  } while (!flag);
  /* do something with 'flag' */
}

问题:

  • 由于编译器的优化,主线程的“标志”可能总是为0(并且会卡在do/while循环中)吗?
  • “易失性”修饰语会有什么不同吗?
  • 如果答案是“取决于编译器提供的特性”,那么在编译时我是否可以用配置脚本检查这个“特性”呢?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-01-19 18:26:31

由于可以假定对齐int的加载是原子操作,所以代码的唯一危险是优化器:允许编译器优化flagmain()中的第一次读取,即将代码转换为

代码语言:javascript
运行
复制
int main(int argc, char **argv)
{
  thread_start_helper(&worker1);
  thread_start_helper(&worker2);
  /* doing something */
  if(!flag) {
      while(1) /* doing something */
  }
  //This point is unreachable and the following can be optimized away entirely.
  /* do something with 'flag' */
}

有两种方法可以确保不会发生这种情况: 1.使flag易失性,这是个坏主意,因为它包含相当多不必要的开销,以及2.引入必要的内存屏障。由于读取int的原子性,以及您只想在flag更改后解释它的值,您应该能够在循环条件之前只使用一个编译器屏障:

代码语言:javascript
运行
复制
int main(int argc, char **argv)
{
  thread_start_helper(&worker1);
  thread_start_helper(&worker2);
  do
  {
    /* doing something */
    barrier();
  } while(!flag)
  /* do something with 'flag' */
}

这里使用的barrier()非常轻量级,是所有可用障碍中最便宜的。

如果您想分析引发flag之前编写的任何其他数据,这是不够的,因为您仍然可能从内存中加载陈旧的数据(因为CPU决定预取值)。有关记忆栅栏、它们的必要性及其使用的全面讨论,请参见https://www.kernel.org/doc/Documentation/memory-barriers.txt

最后,您应该注意到,另一个写入线程可以在flag循环退出后的任何时候修改do{}while()。因此,您应该立即将其值复制到一个影子变量中,如下所示:

代码语言:javascript
运行
复制
int myFlagCopy;
do
{
  /* doing something */
  barrier();
} while(!(myFlagCopy = flag))
/* do something with 'myFlagCopy' */
票数 0
EN

Stack Overflow用户

发布于 2014-01-19 18:19:39

代码很可能按原样工作,但有些脆弱。首先,这取决于对flag的读和写在所使用的处理器上是原子的(并且flag的对齐就足够了)。

我建议要么使用读锁来读取flag的值,要么使用任何线程库的功能来使flag正确原子化。

票数 2
EN

Stack Overflow用户

发布于 2014-01-19 13:15:52

有可能时间在线程之前执行.您必须在使用pthread_join()之前等待线程的执行。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21217123

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档