Потоковая функция не ведет себя так, как я ожидаю - PullRequest
2 голосов
/ 29 июня 2011

Это (примерно) то, что у меня есть:

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 ...

Ответы [ 2 ]

6 голосов
/ 29 июня 2011

Поскольку 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, также обеспечивают правильные барьеры памяти между потоками, поэтому вы видите правильные данные.

2 голосов
/ 29 июня 2011

Такая ситуация может возникнуть, когда функция 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
    }
}
...