C#のTimerは4種類ある
この記事を読んで
「ほーんとりあえずSystem.Timers.Timer
使っとけばOKみたいやな」
って思ってたらハマったのでメモ。
注:自分もまだ原理を理解しきれていないのであやふやな書き方をしている
結論
WindowsFormなら、System.Windows.Forms.Timer
を使おう
元のソース
View
public partial class View : Form, IView
{
Presenter presenter;
public View()
{
InitializeComponent();
presenter = new Presenter(this);
}
// デザイナーで追加したタイマー
Timer IView.timer { get => winFormTimer; }
// タイマー作動時
private void winFormTimer_Tick(object sender, EventArgs e)
{
// プレゼンターのメソッドを呼ぶだけ
presenter.timer_Tick();
}
public void UpdateChart(Point[] points)
{
// 引数からグラフを更新する(割愛)
}
}
Presenter
class Presenter
{
private IView view;
public Presenter(IView _view)
{
view = _view;
}
public void timer_Tick()
{
// 外部と通信
// 通信データを使ってグラフの描画点の配列を作成
var points = new Point[30];
// Viewのグラフ更新メソッドに渡す
view.UpdateChart(points);
}
// 他にも必要に応じてtimerを止めたり動かしたりしてる
}
うーん…このtimer、プレゼンターでしか使っとらんし、System.Windows.Forms.Timer
は精度良くないらしいし、ここはPresenterにSystem.Timers.Timer
を置く形に変更してみよ!
変更後
View
public partial class View : Form, IView
{
Presenter presenter;
public View()
{
InitializeComponent();
presenter = new Presenter(this);
}
public void UpdateChart(Point[] points)
{
// 引数からグラフを更新する(割愛)
}
}
Presenter
class Presenter
{
private IView view;
private System.Timers.Timer timer;
public Presenter(IView _view)
{
view = _view;
timer.Elapsed += timer_Tick;
}
public void timer_Tick(object sender, EventArgs e)
{
// 外部と通信処理
// 通信データを使ってグラフの描画点の配列を作成
var points = new Point[30];
// Viewのグラフ更新メソッドに渡す
view.UpdateChart(points);
}
// 他にも必要に応じてtimerを止めたり動かしたりしてる
}
結果:「外部と通信処理」の箇所で
「メッセージ フィルターはアプリケーションがビジーであることを示しています。」
って言われまくる
なんで?
ぶっちゃけよくわかってない。
なんとなくの理解で書くと…
- 通信処理はViewと同じスレッドで動く
- 通信処理には個数制限(セマフォ的な?)がある
- 通信処理とtimerが同じスレッドで動く場合、timerは通信処理と同期して動く
- そのため、個数制限があふれることはない
- 通信処理とtimerが別スレッドで動く場合、個数制限に関係なくタイマーが動く
- そのため、個数制限に関係なくタイマーは通信を要求し続け、結果アプリケーションがビジーになる
多分こう。
対策
おとなしくSystem.Windows.Forms.Timer
を使うと元に戻った。
あるいは、System.Timers.Timer
のSynchronizingObject
プロパティにViewを設定してもいいらしい。