线程函数的行为与我预期的不同

时间:2011-06-29 08:51:00

标签: c# multithreading mono xamarin.ios

这是(大致)我所拥有的:

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)。

为什么会这样?我想要做的就是确保在执行A类的线程更新功能之前,B类中的更新函数不会执行...

2 个答案:

答案 0 :(得分:6)

由于IsInUpdate只是一个公共字段(而非volatile),因此 没有 保证您所看到的内容;关于你所看到的内容的正常合理规则仅适用于单个线程,并且你没有保护任何这些数据。 start 条件还有一个边缘情况,但我个人会使用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...)
    }
}

通过上述内容,您可以保证任何时候只有一个线程可以拥有锁定,因此如果您“执行某些操作”,您知道它还没有运行其他更新()。如果您需要等待等,还有针对锁定的Wait() / Pulse()方法,或者您可以使用ManualResetEvent / AutoResetEvent等门。

lock之类的东西也确保线程之间存在正确的内存障碍,因此您可以看到正确的数据。

答案 1 :(得分:2)

当尚未调用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
    }
}