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

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

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

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

Q&A

解決済

4回答

2488閲覧

c#で複数のtimerの周期を変更する方法

idaishi

総合スコア12

C#

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

0グッド

0クリップ

投稿2019/04/25 06:46

前提・実現したいこと

c#で複数のtimerを使って,呼び出すごとに周期を変更するということを考えています.

発生している問題・エラーメッセージ

個別にtimerの周期を変更することはできたのですが,配列を使ったらSystem.IndexOutOfRangeExceptionが出てしまいました.
要素の数は超えていないはずなので原因がわかりません

該当のソースコード

c#

using System; using System.Timers; namespace Sample { class Sample { static void Main() { int nmtr = 2; int min_pps = 500; int max_pps = 1500; System.Random rnd = new System.Random(1000); Timer[] timer = new Timer[nmtr]; for (int i = 0; i < nmtr; i++) { timer[i] = new Timer(); timer[i].Interval = rnd.Next(min_pps, max_pps); } for (int i = 0; i < nmtr; i++) { timer[i].Elapsed += (sender, e) => { Console.Write(i); timer[i].Stop(); //timer一時停止 ここでeeror発生 timer[i].Interval = rnd.Next(min_pps, max_pps); //周期変更 timer[i].Start(); //timer再開 }; } for (int i = 0; i < nmtr; i++) { timer[i].Start(); } Console.ReadKey(); } } }

試したこと

以下のように配列を使わずに個別に書いたら成功しましたが,文が長くなるので配列を使いたいです.

using System; using System.Timers; namespace Sample { class Sample { static void Main() { int min_pps = 500; int max_pps = 1500; System.Random rnd = new System.Random(1000); Timer timer1 = new Timer(); Timer timer2 = new Timer(); timer1.Interval = rnd.Next(min_pps, max_pps); timer2.Interval = rnd.Next(min_pps, max_pps); timer1.Elapsed += (sender, e) => { Console.Write(timer1.Interval); timer1.Stop(); //timer一時停止 timer1.Interval = rnd.Next(min_pps, max_pps); //周期変更 timer1.Start(); //timer再開 }; timer2.Elapsed += (sender, e) => { Console.Write(timer2.Interval); timer2.Stop(); //timer一時停止 timer2.Interval = rnd.Next(min_pps, max_pps); //周期変更 timer2.Start(); //timer再開 }; timer1.Start(); timer2.Start();       Console.ReadKey(); } } }

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

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

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

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

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

guest

回答4

0

ベストアンサー

ラムダ式の実行とループは同期しないため、場合によっては実行前にループ変数が次に進んでしまうことがあります。
ループ変数を一度別の変数に代入しておくと回避することができます。

for (int i = 0; i < nmtr; i++) { var idx = i; timer[idx].Elapsed += (sender, e) => { Console.Write(idx); timer[idx].Stop(); //timer一時停止 ここでeeror発生 timer[idx].Interval = rnd.Next(min_pps, max_pps); //周期変更 timer[idx].Start(); //timer再開 }; }

投稿2019/04/25 07:08

編集2019/04/25 07:11
y.nakamura

総合スコア190

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

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

idaishi

2019/04/25 09:52

ありがとうございます。そのことを存じ上げておりませんでした。解決できました。
guest

0

Elapsedイベントの処理で使われているifor (int i = 0; i < nmtr; i++)のカウンタ変数を見ているようなので、2になります。デバッグで確認してみてください。

次のようにiで指定しなければ問題なく動きます。

C#

1using System; 2using System.Timers; 3 4namespace ConsoleApp1 5{ 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int nmtr = 2; 11 int min_pps = 500; 12 int max_pps = 1500; 13 14 System.Random rnd = new System.Random(1000); 15 Timer[] timers = new Timer[nmtr]; 16 17 for (int i = 0; i < nmtr; i++) 18 { 19 var timer = new Timer(); 20 timer.Interval = rnd.Next(min_pps, max_pps); 21 timer.Elapsed += (sender, e) => 22 { 23 Console.WriteLine(timer.Interval); 24 timer.Stop(); //timer一時停止 ここでeeror発生 25 timer.Interval = rnd.Next(min_pps, max_pps); //周期変更 26 timer.Start(); //timer再開 27 }; 28 timers[i] = timer; 29 timers[i].Start(); 30 } 31 Console.ReadKey(); 32 } 33 } 34}

投稿2019/04/25 07:04

BluOxy

総合スコア2663

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

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

idaishi

2019/04/25 23:14

回答ありがとうございます.確かにiは2で固定されてしまいますね.無事解決できました.
guest

0

コピペして試しましたが、少なくともエラー (System.IndexOutOfRangeException) は出ませんけど・・・

【追伸】

下のコメントに「イベントハンドラの引数 sender を使うのがよさそうです。その案を回答欄に書いておきます」とかきましたが、それを以下に書いておきます。

for (int i = 0; i < nmtr; i++) { timer[i].Elapsed += (sender, e) => { Console.Write(i); ((Timer)sender).Stop(); ((Timer)sender).Interval = rnd.Next(min_pps, max_pps); ((Timer)sender).Start(); }; }

投稿2019/04/25 07:03

編集2019/04/25 07:33
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

BluOxy

2019/04/25 07:05

VS2017の出力ウインドウに「例外がスローされました: 'System.IndexOutOfRangeException' (ConsoleApp1.exe の中)」と出力されることを確認しました。
退会済みユーザー

退会済みユーザー

2019/04/25 07:17

失礼しました、[デバッグなしで開始]で動かすと例外は出ませんが[デバッグの開始]では質問者さんの言われる通り例外が出ました。
退会済みユーザー

退会済みユーザー

2019/04/25 07:30

イベントハンドラの引数 sender を使うのがよさそうです。その案を回答欄に書いておきます。
idaishi

2019/04/25 09:51

ありがとうございます。書かれていた方法で解決できました。
guest

0

ループ実行タイミングとイベントハンドラの実行タイミングが異なるので、イベントハンドラ内でループ中の資源は使用すべきではないのではないでしょうか
書くとすれば

C#

1for (int i = 0; i < nmtr; i++) 2{ 3 timer[i].Elapsed += (sender, e) => 4 { 5 Console.Write(Array.IndexOf(timer,sender)); 6 ((Timer)sender).Stop(); //timer一時停止 ここでeeror発生 7 ((Timer)sender).Interval = rnd.Next(min_pps, max_pps); //周期変更 8 ((Timer)sender).Start(); //timer再開 9 }; 10}

Console.Writeはどのタイマーが動作したかの識別で出力されていると思いますが、ここもiを使うべきではありません。

投稿2019/04/25 08:54

YAmaGNZ

総合スコア10258

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

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

idaishi

2019/04/25 23:12

回答ありがとうございます.無事,解決できました.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問