Почему Thread.Sleep() работает в начале функции?

В игре "крестики-нолики" мне нужно выдержать паузу после хода игрока перед ходом компьютера. Вот мой код:

private void pictureBox1_Click(object sender, EventArgs e)
        {
            Player_Move();
            System.Threading.Thread.Sleep(1000);
            Robot_Move();
        }

В результате работы Player_Move в pictureBox должна появиться фигура без задержки, но в моей программе после нажатия на pictureBox сначала выдерживается пауза 1 секунду, а затем одновременно появляется фигура и делается ход компьютера (Robot_Move). То есть задержка как бы работает в начале функции.

В чем моя ошибка и как сделать задержку между Player_Move и Robot_Move?


Ответы (2 шт):

Автор решения: Pavel Mayorov

Проблема тут в том, что где-то внутри библиотеки WinForms (на самом деле ещё глубже, но это не важно) есть цикл обработки событий, который отвечает, в том числе, и за отрисовку.

Можете представить его, для простоты, как-то так:

while (true) {
  ПодождатьСобытия();
  ОбработатьСобытие(); // тут вызывается pictureBox1_Click
  ОтрисоватьФорму();
}

Соответственно, пока вы не вернёте управление из pictureBox1_Click - пользователь не увидит ваших изменений (на самом деле, есть способ "пнуть" отрисовку, но это будет костыль который всех проблем не решает).

Самый простой способ избежать проблемы - сделать метод асинхронным:

private async void pictureBox1_Click(object sender, EventArgs e)
{
    Player_Move();
    await System.Threading.Tasks.Task.Delay(1000);
    Robot_Move();
}

Замечание: async void-методов рекомендуется избегать в общем случае, но обработчики событий UI как раз являются тем самым случаем, для которого async void-методы были добавлены в язык

И не забудьте добавить защитную логику, которая не позволит пользователю сходить не в свой ход!

→ Ссылка
Автор решения: CrazyElf

По-нормальному никакой сложной логики в обработчиках UI не должно быть. Можете там менять какие-то статусы, управление как можно быстрее должно быть возвращено. Иначе будет остановлена отрисовка интерфейса и его реакция на события, пока вы не вернёте управление.

Обработка состояния, вся логика должны быть в каком-то отдельном от UI потоке. Т.е. если произошло событие - вы добавляете это событие в свою очередь обработки событий и выходите из обработчика событий UI.

А свою эту очередь событий и состояний обрабатываете в отдельном потоке, который не мешает UI отрисовывать интерфейс и реагировать на ввод информации, движения и нажатия мыши и прочее.

→ Ссылка