System.Threading.Timerが複数回実行されてしまう問題について

Seiya Masuda 0 評価のポイント
2025-04-11T07:33:37.6766667+00:00

System.Threading.Timerで処理を行なっているシステムで、PCが高負荷になった際に2回実行されてしまう事象が発生しています。

本来は1分回に1回タイマーイベントを通知するように開発していますが、高負荷だとまれにタイマーイベントが2回通知されます。

本事象について、原因と解決策をご教示ください。

.NET
.NET
.NET ソフトウェア フレームワークに基づく Microsoft テクノロジ。
82 件の質問
0 件のコメント コメントはありません
{count} 件の投票

1 件の回答

並べ替え方法: 最も役に立つ
  1. gekka 11,691 評価のポイント MVP
    2025-04-11T08:50:16.7933333+00:00

    System.Threading.Timerのcallbackはスレッドプールで実行され、かつそのスレッドは一つだけではありません。 前のcallbackの処理が終わる前にperiodで指定した時間間隔が経過した場合、別のスレッドで次のcallbackを呼び出します。

    System.Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} タイマーを作ったスレッドのID= {System.Threading.Thread.CurrentThread.ManagedThreadId}");
    
    System.Threading.Timer timer = null;
    timer = new System.Threading.Timer((o) =>
    {
        bool isThreadPool = System.Threading.Thread.CurrentThread.IsThreadPoolThread;
        bool isBackground = System.Threading.Thread.CurrentThread.IsBackground;
    
        System.Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} タイマーのコールバックが実行されているスレッドのID= {System.Threading.Thread.CurrentThread.ManagedThreadId}\tIsThreadPool={isThreadPool}\tIsBackground={isBackground}");
    
        System.Threading.Thread.Sleep(2000); //重い処理としてスレッドをブロック
    
    }, null, 1000, 1000);
    
    System.Threading.Thread.Sleep(-1);
    

    これが問題ならば、コンストラクタまたは、Changeのperiod引数をSystem.Threading.Timeout.Infiniteにして1回だけ呼ばれるようにしておいて、callbackの最後に次の呼び出し時間を登録しなおすようにしてください。

    System.Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} タイマーを作ったスレッドのID= {System.Threading.Thread.CurrentThread.ManagedThreadId}");
    
    System.Threading.Timer timer = null;
    timer = new System.Threading.Timer((o) =>
    {
        bool isThreadPool = System.Threading.Thread.CurrentThread.IsThreadPoolThread;
        bool isBackground = System.Threading.Thread.CurrentThread.IsBackground;
    
        System.Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} タイマーのコールバックが実行されているスレッドのID= {System.Threading.Thread.CurrentThread.ManagedThreadId}\tIsThreadPool={isThreadPool}\tIsBackground={isBackground}");
    
        System.Threading.Thread.Sleep(2000); //重い処理としてスレッドをブロック
    
        timer.Change(1000, System.Threading.Timeout.Infinite); //必要なら次の時間を計算する
    
    }, null, 1000, System.Threading.Timeout.Infinite);
    
    System.Threading.Thread.Sleep(-1);
    
    0 件のコメント コメントはありません

お客様の回答

回答は、質問作成者が [承諾された回答] としてマークできます。これは、ユーザーが回答が作成者の問題を解決したことを知るのに役立ちます。