私のアプリで、特定の時間、たとえば午後4時に継続的に実行されるイベントをトリガーしたいと思います。タイマーを毎秒実行し、時間が午後4:00になったらイベントを実行することを考えました。動作します。しかし、午後4時に一度だけコールバックを取得する方法があり、チェックし続ける必要がないのではないかと思っています。
System.Threading.Timer
クラスを使用して、このようなものはどうですか?
var t = new Timer(TimerCallback);
// Figure how much time until 4:00
DateTime now = DateTime.Now;
DateTime fourOClock = DateTime.Today.AddHours(16.0);
// If it's already past 4:00, wait until 4:00 tomorrow
if (now > fourOClock)
{
fourOClock = fourOClock.AddDays(1.0);
}
int msUntilFour = (int)((fourOClock - now).TotalMilliseconds);
// Set the timer to elapse only once, at 4:00.
t.Change(msUntilFour, Timeout.Infinite);
System.Threading.Timer
を使用すると、TimerCallback
で指定されたコールバックがスレッドプール(非UI)スレッドで実行されることに注意してください。 :00、コードを適切にマーシャリングする必要があります(たとえば、WindowsフォームアプリでControl.Invoke
、またはWPFアプリでDispatcher.Invoke
を使用)。
.NET 4.5以降では、非常にクリーンなソリューションがあります。
public async void ScheduleAction(Action action, DateTime ExecutionTime)
{
await Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
action();
}
Async/awaitを使用しないソリューションを次に示します。
public void Execute(Action action, DateTime ExecutionTime)
{
Task WaitTask = Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
WaitTask.ContinueWith(_ => action);
WaitTask.Start();
}
これはint32の最大値のために約24日間しか機能しないことに注意する必要があります。
Windowsのタスクスケジューラ を使用できます。詳細については、 毎日のトリガーの例 を参照してください。
または、自分で書きたい場合は以下のコードを使用します。
public void InitTimer()
{
DateTime time = DateTime.Now;
int second = time.Second;
int minute = time.Minute;
if (second != 0)
{
minute = minute > 0 ? minute-- : 59;
}
if (minute == 0 && second == 0)
{
// DoAction: in this function also set your timer interval to 24 hours
}
else
{
TimeSpan span = //new daily timespan, previous code was hourly: new TimeSpan(0, 60 - minute, 60 - second);
timer.Interval = (int) span.TotalMilliseconds - 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
void timer_Tick(object sender, EventArgs e)
{
timer.Interval = ...; // 24 hours
// DoAction
}
VoteCoffeesがリードしているのは、イベントベースのコンパクトなソリューションです。
public class DailyTrigger
{
readonly TimeSpan triggerHour;
public DailyTrigger(int hour, int minute = 0, int second = 0)
{
triggerHour = new TimeSpan(hour, minute, second);
InitiateAsync();
}
async void InitiateAsync()
{
while (true)
{
var triggerTime = DateTime.Today + triggerHour - DateTime.Now;
if (triggerTime < TimeSpan.Zero)
triggerTime = triggerTime.Add(new TimeSpan(24,0,0));
await Task.Delay(triggerTime);
OnTimeTriggered?.Invoke();
}
}
public event Action OnTimeTriggered;
}
消費者: `
void Main()
{
var trigger = new DailyTrigger(16); // every day at 4:00pm
trigger.OnTimeTriggered += () =>
{
// Whatever
};
Console.ReadKey();
}
.NETには多くのタイマークラスがありますが、それらはすべて現在の時間と比較して時間を要します。相対的な時間では、タイマーを開始する前に考慮し、タイマーの実行中に監視することがたくさんあります。
オペレーティングシステムは、この複雑さを処理するのに最適な場所にあります。 .NETで実行されるアプリケーションコードはそうではありません。
Windowsの場合、NuGetパッケージ AbsoluteTimer は、絶対時間で期限切れになるオペレーティングシステムタイマーをラップします。
タスクスケジューラは優れたオプションであり、C#で簡単に使用できます http://taskscheduler.codeplex.com/
Timercallbackを使用したDanのソリューションへの反響は、迅速かつ適切なソリューションです。タスクまたはサブルーチンの実行をスケジュールするメソッド内で、次を使用します。
t = New Timer(Sub()
'method call or code here'
End Sub, Nothing, 400, Timeout.Infinite)
「Timeout.Infinite」を使用すると、コールバックが400ミリ秒後に一度だけ実行されるようになります。 VB.Netを使用しています。
このソリューションはどうですか?
Sub Main()
Dim t As New Thread(AddressOf myTask)
t.Start()
Console.ReadLine()
End Sub
Private Sub myTask()
Dim a = "14:35"
Dim format = "dd/MM/yyyy HH:mm:ss"
Dim targetTime = DateTime.Parse(a)
Dim currentTime = DateTime.Parse(Now.ToString(format))
Console.WriteLine(currentTime)
Console.WriteLine("target time " & targetTime)
Dim bb As TimeSpan = targetTime - currentTime
If bb.TotalMilliseconds < 0 Then
targetTime = targetTime.AddDays(1)
bb = targetTime - currentTime
End If
Console.WriteLine("Going to sleep at " & Now.ToString & " for " & bb.TotalMilliseconds)
Thread.Sleep(bb.TotalMilliseconds)
Console.WriteLine("Woke up at " & Now.ToString(format))
End Sub
毎朝午前7時に発砲するためにこのようにしました
bool _ran = false; //initial setting at start up
private void timer_Tick(object sender, EventArgs e)
{
if (DateTime.Now.Hour == 7 && _ran==false)
{
_ran = true;
Do_Something();
}
if(DateTime.Now.Hour != 7 && _ran == true)
{
_ran = false;
}
}