web-dev-qa-db-ja.com

.NET Timerクラスを使用して特定の時間にイベントをトリガーする方法は?

私のアプリで、特定の時間、たとえば午後4時に継続的に実行されるイベントをトリガーしたいと思います。タイマーを毎秒実行し、時間が午後4:00になったらイベントを実行することを考えました。動作します。しかし、午後4時に一度だけコールバックを取得する方法があり、チェックし続ける必要がないのではないかと思っています。

42
Behrooz Karjoo

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を使用)。

43
Dan Tao

.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日間しか機能しないことに注意する必要があります。

27
VoteCoffee

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
}
6
Saeed Amiri

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();
}
4
noontz

.NETには多くのタイマークラスがありますが、それらはすべて現在の時間と比較して時間を要します。相対的な時間では、タイマーを開始する前に考慮し、タイマーの実行中に監視することがたくさんあります。

  • コンピューターがスタンバイ状態になったらどうなりますか?
  • コンピューターの時間が変わったらどうなりますか?
  • 有効期限が夏時間の移行後の場合はどうなりますか?
  • タイマーを開始した後にユーザーがコンピューターのタイムゾーンを変更して、以前は存在していなかった有効期限が切れる前に新しいタイムゾーンが移行した場合はどうなりますか?

オペレーティングシステムは、この複雑さを処理するのに最適な場所にあります。 .NETで実行されるアプリケーションコードはそうではありません。

Windowsの場合、NuGetパッケージ AbsoluteTimer は、絶対時間で期限切れになるオペレーティングシステムタイマーをラップします。

0
Edward Brey

タスクスケジューラは優れたオプションであり、C#で簡単に使用できます http://taskscheduler.codeplex.com/

0
Lex Li

Timercallbackを使用したDanのソリューションへの反響は、迅速かつ適切なソリューションです。タスクまたはサブルーチンの実行をスケジュールするメソッド内で、次を使用します。

    t = New Timer(Sub()
                        'method call or code here'
                  End Sub, Nothing, 400, Timeout.Infinite)

「Timeout.Infinite」を使用すると、コールバックが400ミリ秒後に一度だけ実行されるようになります。 VB.Netを使用しています。

0
Joseph Wu

このソリューションはどうですか?

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
0
Tal Aruety

毎朝午前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;
        }

    }
0
Jon649