質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

3回答

2363閲覧

System.Timers.TimerのEnableがFalseになってしまい、Stop()が利かない

ikarimame

総合スコア37

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2022/12/07 05:21

VisualStudio2022、.NET Framework4.8、C#
にてSystem.Timers.Timerを利用し1秒毎の定期処理を実装しています

Start()実行後初回は即時実行したいため以下のように実装しました。
1.Start()呼び出し前にAutoResetをfalse、Intervalを1とする
2.初回のTimer呼び出しの際にAutoResetをtrue、Intervalを1000に設定する

C#

1 private static System.Timers.Timer T; 2 static void Main(string[] args) 3 { 4 T = new System.Timers.Timer(); 5 T.AutoReset = false; 6 T.Elapsed += Timer_Elapsed; 7 T.Interval = 1; 8 T.Start(); 9 Console.WriteLine(string.Format("Enabled:{0},AutoReset:{1},Interval:{2}", T.Enabled, T.AutoReset, T.Interval)); 10 Console.ReadKey(); 11 T.Stop(); 12 Console.ReadKey(); 13 } 14 private static bool Initialized = false; 15 private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 16 { 17 Console.WriteLine(string.Format("Enabled:{0},AutoReset:{1},Interval:{2}", T.Enabled, T.AutoReset, T.Interval)); 18 if (!Initialized) 19 { 20 Initialized = true; 21 T.AutoReset = true; 22 T.Interval = 1000; 23 } 24 // 処理 25 }

処理の実行タイミングは望んだとおりに動作しているのですが、なぜかStop()でタイマーが停止しません

わかっていることは
Start()した直後はTimer.Enableがtrueなのですが、Timerの処理が呼び出されたときにはfalseとなっており、それが原因でおそらくはStop()が利かないものと思っています。
もしかしたら初回の呼び出しでIntervalを設定した際再度Start()を呼び出さないとならないかとも思いましたが、実際には今のコードでTimerの処理自体は定期的に呼び出されています。(Enableがfalseなのに呼び出しが行われているのもおかしい気はしますが…)

初回は即時実行するタイマー処理を実装し、Stop()で処理を止められるようにしたいのですがどうしたらよいでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/12/07 06:11 編集

Timer 自体がメインスレッドとは別のスレッドで動いているからでは?  ちなみに、Microsoft ドキュメントには "Elapsed イベントは ThreadPool スレッドで発生するため、1 つのスレッドでイベント処理を実行する間に、Stop メソッドの呼び出しが別のスレッドで実行されることがあります。これにより、Stop メソッドを呼び出した後で、Elapsed イベントが発生することがあります" と書いてあります。 単なる思い付きですのでハズレでしたらすみません。
ikarimame

2022/12/07 06:54

>Timer 自体がメインスレッドとは別のスレッドで動いているからでは? Timer自体がメインスレッドとは別のスレッドで動いているためにメインスレッドからStop()を呼び出しても機能しない、という事でしょうか? >ちなみに、Microsoft ドキュメントには "Elapsed イベントは ThreadPool スレッドで発生するため、1 つのスレッドでイベント処理を実行する間に、Stop メソッドの呼び出しが別のスレッドで実行されることがあります。これにより、Stop メソッドを呼び出した後で、Elapsed イベントが発生することがあります" と書いてあります。 これはタイミングにStop()を呼び出した後1度Elapsedが発生するという事なのかなと思っております、今回の件はStop()を呼び出した後もElapsedが発生し続けてしまいます。
退会済みユーザー

退会済みユーザー

2022/12/07 07:18

すみません、上のコメントはハズレだったようです。 Timer を 2 つ使うのはいかがですか? 後で回答欄にその案を書いておきます。
guest

回答3

0

ベストアンサー

System.Timers.Timerの不具合っぽいですね。

https://referencesource.microsoft.com/#System/services/timers/system/timers/Timer.cs

ソースを見ると、AutoReset プロパティを True にしたとき、Enabled プロパティを無視してタイマーを有効にしちゃってます。

T.Start();

を入れればOKかと。

ただ、フラグチェックを入れるのも微妙な気がするので

CSharp

1static void Main(string[] args) { 2 var T = new System.Timers.Timer(); 3 T.Elapsed += Timer_Elapsed; 4 T.Interval = 1000; 5 TimerProc(DateTime.Now); 6 T.Start(); 7 Console.ReadKey(); 8 T.Stop(); 9 Console.ReadKey(); 10} 11 12private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { 13 TimerProc(e.SignalTime); 14} 15 16private static void TimerProc(DateTime SignalTime) { 17 // 処理 18}

のようにしとくといいんじゃないでしょうか。

投稿2022/12/07 06:51

KOZ6.0

総合スコア2626

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2022/12/07 08:36

AutoResetをいじらなければ(trueのままで)いいんじゃないですかね? へたにいじるとドツボにはまってしまうようです^^; [The curious case of System.Timers.Timer | I Came, I Learned, I Blogged](https://evolpin.wordpress.com/2014/04/25/the-curious-case-of-system-timers-timer/) > System.Timers.Timerの不具合っぽいですね。 「そういう仕様です」ということですねw
KOZ6.0

2022/12/07 08:51

>「そういう仕様です」ということですねw ですね!w
ikarimame

2022/12/07 10:17

ソースの提示ありがとうございます、眺めてみたところ AutoReset=falseの場合MyTimerCallbackメソッドの中でenableフィールドをfalseにしているが Enableプロパティを変更したわけではないのでEnableプロパティのsetに実装されているtimerの開放処理が実行されない。 そのためAutoResetをtrueにした際にタイマーが再起動し繰り返しElapsedが発生するようになるが、Enableはfalseという状態が発生してしまうという事のようですね Stopが動作しないのはEnableがfalseだからのようです…… 今回はさほど重い処理を実行するわけではないため、KOZ6.0様に提案していただいた初回はElapsedの呼び出しではなく自前で呼び出すという方向で実装しようと思います。 >「そういう仕様です」ということですねw これは不具合ではないため修正されることはないだろうという意味でしょうか? AutoResetをいじるとEnableが変わらないままタイマーが再起動してしまうのは気持ち悪いですね
KOZ6.0

2022/12/07 10:22

>「そういう仕様です」ということですねw いや、これは冗談ですw 要望を出せば修正してくれるかもしれません。
ikarimame

2022/12/07 12:35

承知いたしました、空気が読めずに申し訳ありません 本件.NET Frameworkではなく.NETですが不具合報告はされているようです、しかし.NETでも未だ修正されていないようです https://github.com/dotnet/runtime/issues/33119
KOZ6.0

2022/12/07 12:53

あれま、上がってましたか。 未解決のものが5Kとかあるから後回しになっちゃってるんでしょうね。
TN8001

2022/12/07 12:56 編集

リロードしたら既にコメントついてたw >> 「そういう仕様です」ということですねw > いや、これは冗談ですw > 要望を出せば修正してくれるかもしれません。 これですかね? [System.Timers.Timer may activate repeating timer in a way in which Stop() would not stop it · Issue #33119 · dotnet/runtime](https://github.com/dotnet/runtime/issues/33119) 先送りされていますが、直す気はありそうに読めます(いいね等しておくと優先度が上がるかもしれません) .NET(Core)だけなのか、.NET Frameworkも含んでいるのかはよくわかりません。 C#(.NET)は破壊的変更を相当嫌う印象を持っています。 なので(おかしいのはわかってるけど)今更変えたくないので仕様として押し通すのかと思っていましたが、最近はそうでもないのかもしれませんね^^
guest

0

「初期化処理をTimer_Elapsed内で行わなければいけない」という縛りは必要ですか? もしその縛りを外してよければ

  1. Main側で初期化処理を実行
  2. タイマーを起動し定期処理を実行
  3. 任意タイミングでタイマーを終了

と書けます。

こうすればTimer_Elapsedは定期実行すべき処理のみに専念できるかと思います。

投稿2022/12/07 09:00

tor4kichi

総合スコア763

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikarimame

2022/12/07 10:20

今回は初回の処理を即時実行し以降は1秒間隔で定期処理する処理を実装したかったため、Timer_Elapsedの初回呼び出しでIntervalを変更するという実装を行った次第です。
guest

0

上の質問に対する私のコメントで、

Timer を 2 つ使うのはいかがですか? 後で回答欄にその案を書いておきます。

・・・と書いた件です。

以下のコードで、

C#

1using System; 2 3namespace ConsoleAppTimer 4{ 5 internal class Program 6 { 7 private static System.Timers.Timer T; 8 private static System.Timers.Timer T2; 9 10 static void Main(string[] args) 11 { 12 T = new System.Timers.Timer(); 13 T.AutoReset = false; 14 T.Elapsed += T_Elapsed; 15 T.Interval = 1; 16 17 // 2022/12/7 17:58 変更 T スタートのタイミング 18 // ここで T をスタートすると T2 の初期化前に T がスタートしてしまう可能性があるので 19 // T.Start(); 20 21 Console.WriteLine(string.Format("T1 / Enabled:{0},AutoReset:{1},Interval:{2}", 22 T.Enabled, T.AutoReset, T.Interval)); 23 24 T2 = new System.Timers.Timer(); 25 T2.AutoReset = true; 26 T2.Elapsed += T2_Elapsed; 27 T2.Interval = 1000; 28 29 // T2 の初期化が完了してからスタート 30 T.Start(); 31 32 Console.ReadKey(); 33 T2.Stop(); 34 Console.ReadKey(); 35 } 36 37 //private static bool Initialized = false; 38 39 private static void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 40 { 41 Console.WriteLine(string.Format("T1 / Enabled:{0},AutoReset:{1},Interval:{2}", 42 T.Enabled, T.AutoReset, T.Interval)); 43 44 T2.Start(); 45 46 //if (!Initialized) 47 //{ 48 // Initialized = true; 49 // T.AutoReset = true; 50 // T.Interval = 1000; 51 //} 52 53 } 54 55 private static void T2_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 56 { 57 Console.WriteLine(string.Format("T2 / Enabled:{0},AutoReset:{1},Interval:{2}", 58 T2.Enabled, T2.AutoReset, T2.Interval)); 59 } 60 } 61} 62

結果は以下のようになります。実行後、最初の Console.ReadKey(); で入力待ちのところに入力して、次の行の T2.Stop(); で Timer を止めたところです。

イメージ説明

投稿2022/12/07 07:28

編集2022/12/07 09:00
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOZ6.0

2022/12/07 08:38

これだと T2 の初期化が完了する前に T2.Start が走ってしまう可能性があります。 T2 の初期化は T.Start() の前にやったほうが良いです。
退会済みユーザー

退会済みユーザー

2022/12/07 08:49

そうですね。書き直しておきます。
ikarimame

2022/12/07 10:18

ありがとうございます、参考にさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問