我有一个对象Foo,它有一个全局变量,Time currentTime
Foo有两种方法可以从不同的线程调用。
update()
{
currentTime = currentTime + timeDelay;
}
restart(Time newTime)
{
currentTime = newTime;
}
我看到重启时的行为,时间正确变化以及currentTime似乎没有重置的其他时间(或者它确实重置但是更新以某种方式将其设置回来。
方法更新大致每隔一秒左右调用一次,而重新启动仅在用户启动重启事件(按下按钮)时发生。我认为这是线程时间问题,欢迎任何有关正在发生的事情的建议或意见。
答案 0 :(得分: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)
线程1调用update,获取currentTime的副本并将其保存在其线程本地内存中。 线程2调用restart,将currentTime设置为newTime。线程2完成。 线程1继续,将currentTime重新分配给其线程本地内存中的currentTime(这是重启之前currentTime的旧值)+ timeDelay。线程1现在完成了。
因此您的重启将失败。还有许多其他情况可能会导致意外行为。始终同步在不同线程之间共享的变量,以避免此类问题。
您应该使用其他人建议的互斥锁。
答案 2 :(得分:0)
在每个函数中添加一些printfs以创建正在发生的事情的日志。
例如,如果在“currentTime = newTime;”之后立即在另一个线程中执行update(),您会发生什么? - 或者甚至更糟 - 期间在该行中的作业。
答案 3 :(得分:0)
从两个线程访问相同的变量(正如您所做)需要某种同步。使用互斥锁来保证一次只有一个线程访问该变量,即:
update()
{
// Lock mutex
currentTime = currentTime + timeDelay;
// unlock mutex
}
// Same idea for restart()
问题在于,在没有同步化原语(如互斥锁)的情况下访问同一变量对于线程来说是有问题的。假设update()读取currentTime,添加,但在它可以存储结果之前,我们切换线程,restart()就是这样。现在我们切换回update(),它将添加的(现在无效的)结果写回currentTime,覆盖restart()的工作。 Mutex通过允许您保证操作是原子的来防止这种情况。谷歌的一些教程 - 你需要知道很多其他的东西,比如死锁。
您如何创建/锁定互斥锁取决于您的操作系统/您要使用的库。原生解决方案是* nix系统上的pthreads,Win32上的关键部分。 (Win32的pthreads实现)Boost库也有一个线程部分。
答案 4 :(得分:0)
在这种情况下,使用互斥锁对我来说太沉重了。我会改用InterlockedExchange和InterlockedAdd。
答案 5 :(得分:0)
您正处于竞争状态,因为在进入关键区域时没有锁定且每个都在更新current_time。每个线程都有一个内存,当线程需要从共享内存中获取内容时,它会将其复制到本地内存中。第一步是先获取一个锁,然后清除它的内存,保证变量将从共享内存中加载。现在在关键区域运行,一旦完成解锁关键区域,保证局部变量将被写入共享内存。由于你没有锁,你无法预测会发生什么。
因为current_time变量只有一个键,所以你需要使用互斥锁。另一种类型的锁定是允许多个键的信号量。
答案 6 :(得分:0)
如果currentTime实际上是一个全局变量,就像你说的那样,你需要一个全局互斥来保护变量。 (PTHREAD_MUTEX_INITIALIZER或BOOST.call_once构造)
在这种情况下,BOOST.Threads示例是不正确的,因为生活在不同线程中的Foo类的两个实例将具有_access互斥锁的不同实例(我真的不喜欢_prefex!)并将锁定它们自己的实例和不保护currentTime变量。
如果currentTime是实例变量,则BOOST.Threads示例是正确的。