Почему 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 шт):
Проблема тут в том, что где-то внутри библиотеки 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-методы были добавлены в язык
И не забудьте добавить защитную логику, которая не позволит пользователю сходить не в свой ход!
По-нормальному никакой сложной логики в обработчиках UI не должно быть. Можете там менять какие-то статусы, управление как можно быстрее должно быть возвращено. Иначе будет остановлена отрисовка интерфейса и его реакция на события, пока вы не вернёте управление.
Обработка состояния, вся логика должны быть в каком-то отдельном от UI потоке. Т.е. если произошло событие - вы добавляете это событие в свою очередь обработки событий и выходите из обработчика событий UI.
А свою эту очередь событий и состояний обрабатываете в отдельном потоке, который не мешает UI отрисовывать интерфейс и реагировать на ввод информации, движения и нажатия мыши и прочее.