C++ C++;线程,共享数据

C++ C++;线程,共享数据,c++,multithreading,synchronization,mutex,C++,Multithreading,Synchronization,Mutex,我有一个应用程序,其中2个线程正在运行。。。当我从一个线程更改一个全局变量时,另一个线程会注意到这个更改吗? 我没有任何同步或互斥系统。。。但是这段代码是否应该一直有效(想象一个名为dataUpdated的全局bool): 线程1: while(1) { if (dataUpdated) updateScreen(); doSomethingElse(); } 线程2: while(1) { if (doSomething()) data

我有一个应用程序,其中2个线程正在运行。。。当我从一个线程更改一个全局变量时,另一个线程会注意到这个更改吗? 我没有任何同步或互斥系统。。。但是这段代码是否应该一直有效(想象一个名为dataUpdated的全局bool):

线程1:

while(1) {
    if (dataUpdated)
        updateScreen();
    doSomethingElse();
}
线程2:

while(1) {
    if (doSomething())
        dataUpdated = TRUE;
}
像gcc这样的编译器是否以一种不检查全局值的方式优化了这段代码,只在编译时考虑它的值(因为它不会在同一时间被更改)


PS:对于一个类似游戏的应用程序来说,在写入值时是否会有读取并不重要。。。重要的是,更改会被另一个线程注意到。

使用volatile关键字向编译器提示该值可以随时更改

volatile int myInteger;
volatile int myInteger;

不,不确定。如果您声明变量为volatile,那么编译器应该生成代码,以便在读取时始终从内存加载变量。

您的解决方案将使用100%的CPU,以及其他问题。谷歌搜索“条件变量”。

克里斯·杰斯特·杨指出:


这只适用于Java1.5+的内存模型。C++标准不解决线程,而易失性不能保证处理器之间的内存一致性。你确实需要一个记忆屏障


因此,唯一正确的答案是实现同步系统,对吗?

下面是一个使用boost条件变量的示例:

bool _updated=false;
boost::mutex _access;
boost::condition _condition;

bool updated()
{
  return _updated;
}

void thread1()
{
  boost::mutex::scoped_lock lock(_access);
  while (true)
  {
    boost::xtime xt;
    boost::xtime_get(&xt, boost::TIME_UTC);
    // note that the second parameter to timed_wait is a predicate function that is called - not the address of a variable to check
    if (_condition.timed_wait(lock, &updated, xt))
      updateScreen();
    doSomethingElse();
  }
}

void thread2()
{
  while(true)
  {
    if (doSomething())
      _updated=true;
  }
}
如果范围正确(“外部”、“全局”等),则会注意到更改。问题是什么时候?按什么顺序

问题是编译器可以并且经常会重新排序逻辑,以填充所有并发管道,作为性能优化

它并没有真正显示在您的特定示例中,因为在赋值周围没有任何其他指令,但是想象一下在bool assign之后声明的函数在赋值之前执行


在维基百科或谷歌搜索“编译器指令重新排序”

使用锁。始终使用锁访问共享数据。将变量标记为volatile将防止编译器优化内存读取,但不会防止其他问题,例如。如果没有锁,就不能保证doSomething()中写入的内存在updateScreen()函数中可见


唯一其他安全的方法是使用,例如显式或隐式地使用联锁*函数。

正如其他人所说,
volatile
关键字是您的朋友。:-)

当您在gcc中禁用了所有优化选项时,您很可能会发现您的代码能够正常工作。在这种情况下(我相信),它将一切都视为易变的,因此每次操作都会在内存中访问变量

启用任何类型的优化后,编译器将尝试使用寄存器中保存的本地副本。根据您的函数,这可能意味着您只能间歇性地看到变量的变化,或者最坏的情况下,永远不会看到

使用关键字
volatile
向编译器指示此变量的内容可以随时更改,并且不应使用本地缓存的副本

尽管如此,通过使用信号量或条件变量,您可能会发现更好的结果

是对该主题的合理介绍。

是的。不,也许吧

首先,正如其他人提到的,您需要使数据更新变得易变;否则,编译器可以自由地从循环中读取它(取决于它是否能看到doSomethingElse没有触及它)


其次,根据您的处理器和订购需求,您可能需要内存屏障。volatile足以保证其他处理器最终会看到更改,但不足以保证更改会按照执行顺序显示。您的示例只有一个标志,因此它并没有真正显示这种现象。如果您需要并使用内存屏障,则不应再需要volatile


并且在基本问题上有良好的背景;我真的不知道有什么类似的东西是专门为线程编写的。谢天谢地,线程不会像硬件外围设备那样频繁地提出这些问题,尽管您描述的情况是这样的(一个表示完成的标志,如果设置了该标志,则其他数据被认为是有效的)正是排序matterns…

使用volatile关键字向编译器提示值可以随时更改

volatile int myInteger;
volatile int myInteger;
以上内容将保证对变量的任何访问都将在不进行任何特定优化的情况下从内存访问,因此,在同一处理器上运行的所有线程都将“看到”变量的更改,其语义与代码读取时相同


Chris Jester Young指出,在多处理器系统中,可能会出现这种可变值变化的一致性问题。这是一个考虑因素,取决于平台

实际上,相对于平台,有两个方面需要考虑。它们是内存事务的一致性和原子性

原子性实际上是单处理器和多处理器平台的一个考虑因素。问题的出现是因为变量本质上可能是多字节的,问题是一个线程是否可以看到该值的部分更新。ie:某些字节已更改,上下文切换,中断线程读取的值无效。对于自然机器字大小或更小且自然对齐的单个变量,不应引起关注。具体来说,int类型在这方面应该总是可以的,只要它是对齐的-这应该是c的默认情况