Осуществление паузы метода, не блокируя GUI. C#

Существует также расширение PHP: Pip - Python в PHP, который я никогда не пробовал, но отмечал его для просто такого случая

5
задан Windos 14 September 2009 в 01:45
поделиться

4 ответа

Проблема в том, что любой код, который вы запускаете пользовательский интерфейс заблокирует пользовательский интерфейс и заморозит программу. Когда ваш код работает (даже если он работает Thread.Sleep ), сообщения (такие как Paint или Click), отправленные в пользовательский интерфейс, не будут обрабатываться (до тех пор, пока управление не вернется в цикл сообщений при выходе из события. обработчик), вызывая его зависание.

Лучший способ сделать это - запустить в фоновом потоке, а затем Вызвать поток пользовательского интерфейса между засыпанием, например:

//From the UI thread,
ThreadPool.QueueUserWorkItem(delegate {
    //This code runs on a backround thread.
    //It will not block the UI.
    //However, you can't manipulate the UI from here.
    //Instead, call Invoke.
    Invoke(new Action(delegate { cardImage1.Image = playDeck.deal().show(); }));
    Thread.Sleep(100);

    Invoke(new Action(delegate { cardImage2.Image = playDeck.deal().show(); }));
    Thread.Sleep(100);

    Invoke(new Action(delegate { cardImage3.Image = playDeck.deal().show(); }));
    Thread.Sleep(100);

    //etc...
});
//The UI thread will continue while the delegate runs in the background.

В качестве альтернативы, вы может сделать таймер и показывать каждое изображение в следующий тик таймера. Если вы используете таймер, все, что вам нужно сделать вначале, это запустить таймер; не ждите, иначе возникнет та же проблема.

17
ответ дан 18 December 2019 в 06:23
поделиться

Обычно я бы просто рекомендовал такую ​​функцию для выполнения паузы, позволяя пользовательскому интерфейсу быть интерактивным.

  private void InteractivePause(TimeSpan length)
  {
     DateTime start = DateTime.Now;
     TimeSpan restTime = new TimeSpan(200000); // 20 milliseconds
     while(true)
     {
        System.Windows.Forms.Application.DoEvents();
        TimeSpan remainingTime = start.Add(length).Subtract(DateTime.Now);
        if (remainingTime > restTime)
        {
           System.Diagnostics.Debug.WriteLine(string.Format("1: {0}", remainingTime));
           // Wait an insignificant amount of time so that the
           // CPU usage doesn't hit the roof while we wait.
           System.Threading.Thread.Sleep(restTime);
        }
        else
        {
           System.Diagnostics.Debug.WriteLine(string.Format("2: {0}", remainingTime));
           if (remainingTime.Ticks > 0)
              System.Threading.Thread.Sleep(remainingTime);
           break;
        }
     }
  }

Но, похоже, есть некоторые сложности в использовании такого решения, когда оно вызывается из обработчика событий, такого как нажатие кнопки. Я думаю, что система хочет, чтобы обработчик события нажатия кнопки вернулся, прежде чем он продолжит обработку других событий, потому что, если я попытаюсь снова щелкнуть, пока обработчик событий все еще работает, кнопка снова нажимается, даже если я пытаюсь перетащить форму, а не нажмите на кнопку.

Итак, вот моя альтернатива. Добавьте таймер в форму и создайте класс дилера для обработки карт, взаимодействуя с этим таймером. Задайте свойство Interval таймера, чтобы оно соответствовало интервалу, с которым вы хотите раздавать карты. Вот мой пример кода.

public partial class Form1 : Form
{

  CardDealer dealer;

  public Form1()
  {
     InitializeComponent();
     dealer = new CardDealer(timer1);
  }

  private void button1_Click(object sender, EventArgs e)
  {
     dealer.QueueCard(img1, cardImage1);
     dealer.QueueCard(img2, cardImage2);
     dealer.QueueCard(img3, cardImage1);
  }
}

class CardDealer
{
  // A queue of pairs in which the first value represents
  // the slot where the card will go, and the second is
  // a reference to the image that will appear there.
  Queue<KeyValuePair<Label, Image>> cardsToDeal;
  System.Windows.Forms.Timer dealTimer;

  public CardDealer(System.Windows.Forms.Timer dealTimer)
  {
     cardsToDeal = new Queue<KeyValuePair<Label, Image>>();
     dealTimer.Tick += new EventHandler(dealTimer_Tick);
     this.dealTimer = dealTimer;
  }

  void dealTimer_Tick(object sender, EventArgs e)
  {
     KeyValuePair<Label, Image> cardInfo = cardInfo = cardsToDeal.Dequeue();
     cardInfo.Key.Image = cardInfo.Value;
     if (cardsToDeal.Count <= 0)
        dealTimer.Enabled = false;
  }

  public void QueueCard(Label slot, Image card)
  {
     cardsToDeal.Enqueue(new KeyValuePair<Label, Image>(slot, card));
     dealTimer.Enabled = true;
  }
}
3
ответ дан 18 December 2019 в 06:23
поделиться

Дешевым выходом было бы выполнение цикла с вызовами Application.DoEvents (), но лучшей альтернативой было бы установить System.Windows.Forms.Timer, который вы бы остановили после первого раза это истекает. В любом случае вам понадобится индикатор, чтобы сообщить обработчикам событий пользовательского интерфейса игнорировать ввод. Вы даже можете просто использовать для этой цели свойство timer.Enabled, если оно достаточно простое.

3
ответ дан 18 December 2019 в 06:23
поделиться

Я бы попробовал поместить код, который обрабатывает колоду (и вызывает Thread.Sleep) в другом потоке.

2
ответ дан 18 December 2019 в 06:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: