线程函数的行为并不像我所期望的那样
本文关键字:期望 函数 线程 | 更新日期: 2023-09-27 18:00:35
这是(大致)我所拥有的:
class A
{
public bool IsInUpdate = false;
public void Update()
{
IsInUpdate = true;
//(...do stuff...)
IsInUpdate = false;
}
}
class B
{
A a_inst;
System.Threading.Thread physicsThread = null;
void Draw()
{
physicsThread = new System.Threading.Thread(a_inst.Update);
physicsThread.Start();
}
void Update()
{
while(physicsThread.IsAlive)
{
// Right here there can be cases where physicsThread.IsAlive is true but IsInUpdate is false, how does that happen?
}
(...do stuff...)
}
}
问题在代码的注释中。基本上,物理线程实例表示它是活动的,但它正在调用的函数显然已经完成了调用(可以从bool设置为false中看出)。
你知道为什么会这样吗?我只想确保B类中的更新函数在A类的线程更新函数执行之前不会执行。。。
由于IsInUpdate
只是一个公共字段(而且是非volatile
),因此对您所看到的内容没有保证;关于您所看到的内容的正常合理规则只适用于单个线程,并且您没有保护任何这些数据。开始条件也有一个边缘情况,但就我个人而言,我会使用lock
(如果您需要等待它完成),或者如果您只需要知道它是否处于活动状态,则可能使用Interlocked
。
例如:
class A
{
private readonly object syncLock = new object();
public object SyncLock { get { return syncLock; } }
public void Update()
{
lock(SyncLock)
{
//(...do stuff...)
}
}
}
和
void Update()
{
lock(a_inst.SyncLock)
{
(...do stuff...)
}
}
有了以上内容,就可以保证在任何时候只有一个线程拥有锁,所以如果你开始"做事情",就会知道它不会同时运行另一个Update()。如果您需要等待等,也有针对锁的Wait()
/Pulse()
方法,或者您可以使用诸如ManualResetEvent
/AutoResetEvent
之类的门。
像lock
这样的东西还可以确保线程之间有正确的内存屏障,这样您就可以看到正确的数据。
当尚未调用Update
函数时,可能会发生这种情况。仅仅因为您在线程上调用了Start
,并不意味着它会立即执行它的主函数。我不能100%确定是否有一个微小的机会窗口,线程仍然活跃,但主函数已经完成执行。
基本上,您希望查看ManualResetEvent
或AutoResetEvent
,以表示您的线程已完成工作。或者,您可以在Update()
完成并且B
可以订阅后引发的事件可能就足够了。像这样:
class A
{
public event EventHandler UpdateFinished;
public void Update()
{
... do work
var handler = UpdateFinished;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
class B
{
public void Draw()
{
a_inst.UpdateFinished += HandleUpdateFinished;
... start your thread
}
private void HandleUpdateFinished(object sender, EventArgs e)
{
... do whatever
}
}