Как отложить программу на определенное количество миллисекунд или до нажатия клавиши?
Мне нужно отложить выполнение моей программы на указанное количество миллисекунд, но я также хочу, чтобы пользователь мог избежать ожидания при нажатии клавиши. Если ни одна клавиша не нажата, программа должна ожидать указанное количество миллисекунд.
Я использую 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 для пользовательского интерфейса.
Есть много способов решить вашу проблему, но с учетом структуры самое простое, что приходит мне в голову:
- Блокировка вашего пользовательского интерфейса (т.е.
this.Enabled = false
) - Настройка таймера (то есть:
timer.Tick += ContinueUITimer();
) - Настройка обработчика клавиатуры (т.е.
this.KeyDown += ContinueUIKeyboard
)
С функциями ContinueUI, как это:
void ContinueUIXxx(...)
{
timer.Tick -= ContinueUITimer;
this.KeyDown -= ContinueUIKeyboard;
this.Enabled = true;
... whatever else in the continuation;
}