线程函数的行为并不像我所期望的那样

本文关键字:期望 函数 线程 | 更新日期: 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%确定是否有一个微小的机会窗口,线程仍然活跃,但主函数已经完成执行。

基本上,您希望查看ManualResetEventAutoResetEvent,以表示您的线程已完成工作。或者,您可以在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
    }
}