C++多线程问题

3

我有一个名为Foo的对象,它有一个全局变量Time currentTime。

Foo有两个方法,这两个方法从不同的线程中调用。

update()
{
    currentTime = currentTime + timeDelay;
}

restart(Time newTime)
{
    currentTime = newTime;
}

我看到在重新启动时,时间会正确更改,但有时候currentTime似乎没有重置(或者它已经被重置,但update方法会将其重新设置回去)。update方法大约每秒钟调用一次,而重新启动仅在用户发起重新启动事件(按下按钮)时发生。我认为这是一个线程定时问题,欢迎任何关于发生了什么的建议或评论。
7个回答

9

你这里肯定存在竞争条件。最直接的解决方案是使用锁来保护共享变量currentTime的使用。我在这里使用Boost.Threads互斥锁类:

class Foo
{
  boost::mutex _access;
  update()
  {
    boost::mutex::scoped_lock lock(_access);
    currentTime = currentTime + timeDelay;
  }

  restart(Time newTime)
  {
    boost::mutex::scoped_lock lock(_access);
    currentTime = newTime;
  }
};

1

线程1调用update,获取currentTime的副本并将其保存在其线程本地内存中。 线程2调用restart,将currentTime设置为newTime。线程2完成。 线程1继续执行,重新将currentTime分配给其线程本地内存中的currentTime(即您重启调用之前的旧currentTime值)+时间延迟。线程1现在完成。

因此,您的重启将失败。还有许多其他可能导致意外行为的情况。始终同步在不同线程之间共享的变量,以避免此类问题。

建议您使用其他人提供的互斥锁。


0

从两个线程访问同一个变量(就像你现在做的)需要一些形式的同步。使用互斥锁来确保只有一个线程在同一时间访问该变量,例如:

update()
{
    // Lock mutex
    currentTime = currentTime + timeDelay;
    // unlock mutex
}
// Same idea for restart()

问题在于,如果没有像互斥锁这样的同步原语,访问相同的变量会对线程造成问题。比如update()读取currentTime,进行加法运算,但在它存储结果之前,我们切换到了另一个线程,restart()开始执行。现在我们又回到了update(),它将(现在无效的)加法结果写回到currentTime中,覆盖了restart()的工作。互斥锁通过允许您保证操作是原子的来防止这种情况发生。可以搜索一些教程 - 您需要了解很多其他内容,例如死锁。
如何创建/锁定互斥锁取决于您的操作系统/要使用的库。在*nix系统上,本地解决方案是pthreads,在Win32上是关键部分。(pthreads实现存在于Win32上)Boost库还有一个线程部分。

0
在每个函数中添加一些printf语句以创建正在发生的日志。
例如,如果update()在“currentTime = newTime;”之后立即在另一个线程中执行,您希望发生什么? - 或者更糟糕的是 - 在该行中进行分配期间。
完成后,请查看: {{link1:http://en.wikipedia.org/wiki/Mutual_exclusion}}

0

在这种情况下,使用互斥锁感觉太重了。我会改用InterlockedExchange和InterlockedAdd。


0

由于进入关键区域时没有锁定,每个线程都在更新当前时间,因此您遇到了竞争条件。每个线程都有一个内存,当线程需要从共享内存中获取某些内容时,它会将其复制到本地内存中。第一步是先获取锁,然后清除内存,确保变量将从共享内存中加载。现在在关键区域中操作,完成后解锁关键区域,确保本地变量将写入共享内存。由于您没有锁定,无法预测会发生什么。

对于您的情况,使用互斥锁将是所需的,因为当前时间变量只有一个关键字。另一种类型的锁定是信号量,它允许多个关键字。


0
如果currentTime确实像你所说的是一个全局变量,那么你需要一个全局互斥锁来保护这个变量。(使用PTHREAD_MUTEX_INITIALIZER或BOOST.call_once结构)
在这种情况下,BOOST.Threads示例是不正确的,因为生活在不同线程中的Foo类的两个实例将具有不同的_access互斥锁实例(我真的不喜欢前缀!),并且将锁定自己的实例而不保护currentTime变量。
如果currentTime是一个实例变量,则BOOST.Threads示例是正确的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接