Как отложить программу на определенное количество миллисекунд или до нажатия клавиши?

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

Я использую Thread.Sleep остановить программу (что в контексте моей программы, я думаю, нормально, поскольку пользовательский интерфейс настроен на минимизацию во время выполнения основного метода).

Я думал о том, чтобы сделать что-то вроде этого:

while(GetAsyncKeyState(System.Windows.Forms.Keys.Escape) == 0 || waitTime > totalWait)
{
    Thread.Sleep(100);
    waitTime += 100;
}

Как Thread.Sleep будет ждать, по крайней мере, до времени, указанного до пробуждения потока, очевидно будет большая нежелательная дополнительная задержка, поскольку она увеличивается в цикле while.

Есть ли какой-нибудь метод, который будет спать в течение определенного периода времени, но только когда условие выполняется? Или приведенный выше пример является "правильным" способом сделать это, но использовать более точный метод Sleep? Если да, то какой метод я могу использовать?

Заранее спасибо за помощь.

Редактировать ---- Возможная идея...

DateTime timeAtStart = DateTime.Now;
int maxWaitTime = 15000;

while (true)
{
    if (GetAsyncKeyState(System.Windows.Forms.Keys.Escape) != 0)
    {
        break;
    }

    if ((DateTime.Now - timeAtStart).TotalMilliseconds >= maxWaitTime)
    {
        break;
    }
}

Это не использует какой-либо таймер, но похоже, что он может работать, какие-либо предложения?

Редактировать 2: Вышеприведенное работает для меня и теперь позволяет мне прервать ожидание, когда нажата кнопка escape Я заметил, что задержка более точная, чем при использовании Thread.Sleep тоже!

3 ответа

Решение

Первый пример использует Timer, ManuelResetEvent и ловушку глобальной клавиатуры:

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;

namespace WindowsFormsApplication1
{
    static class Program
    {
        private static System.Threading.Timer _timer;
        private static ManualResetEvent _signal;

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            _signal = new ManualResetEvent(false);

            _timer = new System.Threading.Timer(Timer_Signaled, null, 15000, 0);

            _signal.WaitOne();
            _signal.Reset();

            Application.Run(new Form1());
        }

        private static void Timer_Signaled(object state)
        {
            _signal.Set();
        }
    }
}

Когда вы подключитесь к клавиатуре и нажмете ESC, просто вызовите: _signal.Set(). Этот первый пример просто чтобы дать вам идею.

Второй образец:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    static class Program
    {

        [DllImport("user32.dll")]
        static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            int maxWaitTime = 15000;

            int tc = System.Environment.TickCount;

            while (true)
            {
                System.Threading.Thread.Sleep(1000);

                if (System.Environment.TickCount - tc > maxWaitTime)
                {
                    break;
                }

                if (GetAsyncKeyState(Keys.Escape) > 0)
                {
                    break;
                }
            }

            Application.Run(new Form1());
        }
    }
}

Редакция:

Первый пример более надежен, так как хук клавиатуры использует обратный вызов, чтобы сообщить, какая клавиша была нажата. Второй пример работает как "Потяните", и это может произойти не при каждом нажатии клавиши.

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

Запустите таймер Windows Forms с помощью тикового обработчика, который будет запускать все, что вы хотите, а также ключевого обработчика событий, который запустит его и остановит таймер.

Глядя на ваш код, я предполагаю, что вы используете Windows Forms для пользовательского интерфейса.

Есть много способов решить вашу проблему, но с учетом структуры самое простое, что приходит мне в голову:

  1. Блокировка вашего пользовательского интерфейса (т.е. this.Enabled = false)
  2. Настройка таймера (то есть: timer.Tick += ContinueUITimer();)
  3. Настройка обработчика клавиатуры (т.е. this.KeyDown += ContinueUIKeyboard)

С функциями ContinueUI, как это:

 void ContinueUIXxx(...) 
 {   
   timer.Tick -= ContinueUITimer;   
   this.KeyDown -= ContinueUIKeyboard;   
   this.Enabled = true;
    ... whatever else in the continuation; 
 }
Другие вопросы по тегам