Создайте .net System.Timers.Timer немедленно.
Я использую таймер для периодического запуска события на достаточно длинном интервале (2 минуты). Это работает нормально. Однако я хотел бы, чтобы событие срабатывало сразу же после создания таймера (вместо ожидания 2 минуты).
Обратите внимание, что я не могу это сделать, просто вызвав метод, так как требуется некоторое время для запуска и блокировка приложения. Мне нужно, чтобы таймер запускался как обычно и запускал событие в отдельном потоке.
Лучший способ, которым я могу это сделать в данный момент, - подклассификация таймера и создание метода TriggerManually
, который бы сделал что-то вроде этого:
- Включить автоматический reset off
- Установите интервал в 1 мс
- Включить таймер
Это немедленно вызовет прошедшее событие, и я смогу вернуть все настройки в нормальное состояние.
Кажется, немного круглая. Есть ли лучший способ сделать это?
Ответы
Ответ 1
Вместо этого вы можете использовать System.Threading.Timer?
Он имеет конструктор, который позволяет вам выбирать интервал, а также задержку (которая может быть установлена на 0 для немедленного начала).
http://msdn.microsoft.com/en-us/library/2x96zfy7.aspx
Ответ 2
Не могли бы вы просто вызвать обработчик событий для прошедшего события вручную?
Даже если вы ожидали, что он будет выполняться в потоке пула потоков, вы можете вызвать его.
class Blah
{
private Timer mTimer;
public Blah()
{
mTimer = new Timer(120000);
ElapsedEventHandler handler = new ElapsedEventHandler(Timer_Elapsed);
mTimer.Elapsed += handler;
mTimer.Enabled = true;
//Manually execute the event handler on a threadpool thread.
handler.BeginInvoke(this, null, new AsyncCallback(Timer_ElapsedCallback), handler);
}
private static void Timer_Elapsed(object source, ElapsedEventArgs e)
{
//Do stuff...
}
private void Timer_ElapsedCallback(IAsyncResult result)
{
ElapsedEventHandler handler = result.AsyncState as ElapsedEventHandler;
if (handler != null)
{
handler.EndInvoke(result);
}
}
}
Ответ 3
Мне понравился ответ Роб Кука, поэтому я построил небольшой класс EagerTimer
, который подклассы System.Timers.Timer
и добавляет эту функциональность. (С подсказками эти статьи)
Я знаю, что я мог бы использовать System.Threading.Timer
вместо этого, но это просто и хорошо работает в моем приложении.
EagerTimer
/// <summary>
// EagerTimer is a simple wrapper around System.Timers.Timer that
// provides "set up and immediately execute" functionality by adding a
// new AutoStart property, and also provides the ability to manually
// raise the Elapsed event with RaiseElapsed.
/// </summary>
public class EagerTimer : Timer
{
public EagerTimer()
: base() { }
public EagerTimer(double interval)
: base(interval) { }
// Need to hide this so we can use Elapsed.Invoke below
// (otherwise the compiler complains)
private event ElapsedEventHandler _elapsedHandler;
public new event ElapsedEventHandler Elapsed
{
add { _elapsedHandler += value; base.Elapsed += value; }
remove { _elapsedHandler -= value; base.Elapsed -= value; }
}
public new void Start()
{
// If AutoStart is enabled, we need to invoke the timer event manually
if (AutoStart)
{
this._elapsedHandler.BeginInvoke(this, null, new AsyncCallback(AutoStartCallback), _elapsedHandler); // fire immediately
}
// Proceed as normal
base.Start();
}
private void AutoStartCallback(IAsyncResult result)
{
ElapsedEventHandler handler = result.AsyncState as ElapsedEventHandler;
if (handler != null) handler.EndInvoke(result);
}
// Summary:
// Gets or sets a value indicating whether the EagerTimer should raise
// the System.Timers.Timer.Elapsed event immediately when Start() is called,
// or only after the first time it elapses. If AutoStart is false, EagerTimer behaves
// identically to System.Timers.Timer.
//
// Returns:
// true if the EagerTimer should raise the System.Timers.Timer.Elapsed
// event immediately when Start() is called; false if it should raise the System.Timers.Timer.Elapsed
// event only after the first time the interval elapses. The default is true.
[Category("Behavior")]
[DefaultValue(true)]
[TimersDescription("TimerAutoStart")]
public bool AutoStart { get; set; }
/// <summary>
/// Manually raises the Elapsed event of the System.Timers.Timer.
/// </summary>
public void RaiseElapsed()
{
if (_elapsedHandler != null)
_elapsedHandler(this, null);
}
}
Тесты устройств
[TestClass]
public class Objects_EagerTimer_Tests
{
private const int TimerInterval = 10; // ms
private List<DateTime> _timerFires = new List<DateTime>();
private DateTime _testStart;
[TestInitialize]
public void TestSetup()
{
_timerFires.Clear();
_testStart = DateTime.Now;
}
[TestMethod]
public void Objects_EagerTimer_WithAutoStartDisabled()
{
// EagerTimer should behave as a normal System.Timers.Timer object
var timer = new EagerTimer(TimerInterval);
timer.AutoReset = false;
timer.Elapsed += timerElapsed;
timer.Start();
// Wait (not enough time for first interval)
Thread.Sleep(5);
Assert.IsFalse(_timerFires.Any());
// Wait a little longer
Thread.Sleep(TimerInterval);
Assert.AreEqual(1, _timerFires.Count);
}
[TestMethod]
public void Objects_EagerTimer_WithAutoStartEnabled()
{
// EagerTimer should fire immediately on Start()
var timer = new EagerTimer(TimerInterval);
timer.AutoReset = false;
timer.AutoStart = true;
timer.Elapsed += timerElapsed;
timer.Start();
// Wait (not enough time for first interval)
Thread.Sleep(5);
Assert.IsTrue(_timerFires.Any());
// Wait a little longer, now it will have fired twice
Thread.Sleep(TimerInterval);
Assert.AreEqual(2, _timerFires.Count);
}
[TestMethod]
public void Objects_EagerTimer_WhenRaisingManually()
{
// EagerTimer should fire immediately on Start()
var timer = new EagerTimer(TimerInterval);
timer.AutoReset = false;
timer.AutoStart = false;
timer.Elapsed += timerElapsed;
Assert.IsFalse(_timerFires.Any());
timer.RaiseElapsed();
Assert.IsTrue(_timerFires.Any());
}
private void timerElapsed(object sender, ElapsedEventArgs e) {
_timerFires.Add(DateTime.Now);
}
}