経過イベントの処理中にタイマーをブロックする方法は?
-
03-07-2019 - |
質問
経過したイベントハンドラを同時に処理する必要がないタイマーがあります。ただし、1つの経過イベントを処理すると、他のユーザーに干渉する可能性があります。 以下のソリューションを実装しましたが、何かがおかしいと感じました。タイマーを異なる方法で使用するか、スレッド空間内で別のオブジェクトを使用する必要があるようです。定期的にステータスを確認する必要があるため、タイマーが最適であるように見えましたが、時々チェックが私の間隔よりも長くかかることがあります。これはこれにアプローチする最良の方法ですか?
// member variable
private static readonly object timerLock = new object();
private bool found = false;
// elsewhere
timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
timer.Elapsed = Timer_OnElapsed;
timer.Start();
public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
{
lock(timerLock)
{
if (!found)
{
found = LookForItWhichMightTakeALongTime();
}
}
}
解決
AutoResetをfalseに設定し、処理の完了後にタイマーを明示的にリセットできます。もちろん、それをどのように処理するかは、タイマーがどのように動作するかによって異なります。この方法で実行すると、タイマーが実際に指定された間隔からずれます(停止および再起動する場合と同様)。メカニズムにより、各間隔を起動して処理することができますが、ハンドラの呼び出しを引き起こすタイマーの期限切れに近いところで処理される未処理のイベントのバックログが発生する可能性があります。
timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
timer.Elapsed += Timer_OnElapsed;
timer.AutoReset = false;
timer.Start();
public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
{
if (!found)
{
found = LookForItWhichMightTakeALongTime();
}
timer.Start();
}
他のヒント
通常、処理中にタイマーを停止し、try / finallyブロックに入り、完了したらタイマーを再開します。
LookForItWhichMightTakeALongTime()
に時間がかかる場合、UIがロックされるため、 System.Windows.Forms.Timer
を使用しないことをお勧めします。スレッドとユーザーがフリーズしたと考えてアプリケーションを強制終了する場合があります。
使用できるのは BackgroundWorker
です(必要に応じて Timer
も使用できます)。
public class MyForm : Form
{
private BackgroundWorker backgroundWorker = new BackgroundWorker();
public MyForm()
{
InitializeComponents();
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted +=
backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = LookForItWhichMightTakeALongTime();
}
private void backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
found = e.Result as MyClass;
}
}
また、必要に応じて Timer
からでも、任意の場所から RunWorkerAsync()
を呼び出すことができます。また、実行中に RunWorkerAsync()
を呼び出すと例外がスローされるため、 BackgroundWorker
が既に実行されているかどうかを確認してください。
private void timer_Tick(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
backgroundWorker.RunWorkerAsync();
}
timer.enabled = false
または
timer.stop();
and
timer.enabled = true
または
timer.start();
System.Threading.Timerを次のように使用します
class Class1
{
static Timer timer = new Timer(DoSomething,null,TimeSpan.FromMinutes(1),TimeSpan.FromMinutes(1));
private static void DoSomething(object state)
{
timer = null; // stop timer
// do some long stuff here
timer = new Timer(DoSomething, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
}