C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[12]: System.Timers.Timerでのイベント処理 [1]


(過去ログ 22 を表示中)

[トピック内 24 記事 (21 - 24 表示)]  << 0 | 1 >>

■9706 / inTopicNo.21)  Re[10]: System.Timers.Timerでのイベント処理
  
□投稿者/ れい (160回)-(2007/11/02(Fri) 16:14:41)
No9689 (nbmyou さん) に返信
>>でもスレ主さんは理解できたのかな?
>
> すみません。正直、半分も理解できていないと思います。
> そこで、数点質問させていただけますでしょうか。

あうー
だめでしたか。

> 質問の前に念のため、作成しようとしている監視の内容について概要をお伝えいたします。
> …
> 大雑把にこんな感じのものです。

大体わかったかんじです。

> @
> 上記に示したような内容の処理内容なので、重くてもスピード重視というよりは、
> 基本的に軽く動作するように実装したく思っております。それであれば、

ただの監視+DB登録なので、
多少遅れても問題なさそうですから、
スレッド一つに制限したほうがいいでしょう。

> A
> 無知で申し訳ないのですが、「ワーカースレッド」とは、
> 「作業を行うスレッド」というような意味合いで間違いありませんでしょうか。

OSから見たら普通のスレッドです。

コントロールスレッドとか、GUI用スレッドとか、
そういったものではなく、
作業用のスレッド、という意味合いです。

> A
> 上記の内容は、
>  ・System.Timers.Timerを使用する。
>  ・Sleepを使用する。(Thread.Sleepをタイマー代わりに使用するということでしょうか?)
> という2択を示してくださった、という認識で間違いありませんでしょうか。

この辺、うまく整理できません。
二つの概念がごっちゃになっています。

(1) 軽くするために暇なときにはスレッドを停止させないといけない。
(2) 5分に1回処理するために時刻を測らないといけない。

この二つの概念を実装しないといけません。

System.Timers.Timerを使うと、(1)、(2)共に自動で満たされます。
ですが必然的に再入が起こります。
再入を防ぐには、Interlockなど使う必要があります。

System.Timers.Timerは.Net依存ですが、
そうではない一般的方法で(1)を満たすには、
Sleepを使うか、同期オブジェクトのWaitを使うのが普通です。

Sleepを使うなら、例えば1秒間隔でSleepから復帰し、
時刻を確認して処理が必要な時刻になっていたら処理をし、
そうでないならまたSleepする、という方法で(2)を満たすことができます。

Waitを使うなら、タイムアウトを使ってSleepと同じように定期的に時刻を確認するか、
他のスレッドからイベント通知するという手で(2)を満たすことができます。
他のスレッドからイベント通知するなら、その「他のスレッド」は
System.Timers.Timerを使って、イベントにはAutoResetEventを使うのが楽です。

> 上記のどちらかの方法をお勧めしてくださっているのですよね?

どちらかではなく、Sleepを使う方法も加えて、3つの方法を書いていました。
前回聞いた範囲内でお勧めできそうなものは、その3つくらいだったので。

> これら上記どちらの方法を使用しても、同じ処理(タイマイベントで処理実行時、次のタイマイベントが発生しても、後のイベントは何もしない)を
> 行うことができるという認識で間違いありませんでしょうか。

そうですね。
私の述べた3つの方法はどれもそれをするための方法です。

あとはどう例外処理をするのかとか、
処理時間のほうが監視時間より長くなった場合どうするのかとか、
そういったことを考慮して選べばよいと思います。

> (処理に2分かかったとすると、実際には次の処理は7分後となる?)
> できれば、5分と設定したら、5分ごとに処理が行われるようにしたいので、
> それであればSleepを使う方法は、今回は避けようかと思っています。

これは、工夫次第ですよね。Sleepで時間を測れとは言っていません。
Sleepは時間を測るものではなく、待つものですから。
1秒間スリープしては現在時刻を見て、前回処理時刻と比較してもいいわけです。
用途によっては10秒間でも1分間隔でも良いかもしれません。

>>処理スピードがファイルを置くスピードよりも十分に速い事が保障できるのであれば、
>>仕組みを簡略化する事も可能でしょうし。
> 特に保障できるかは定かではありませんが、
> 簡略化するとは、たとえばどのような方法がありますでしょうか。

これに関しては、私は懐疑的です。
まず、処理スピードがファイルを置くスピードよりも十分に速い事を保証するのは困難です。
保証するコストと簡略化するコストでは、前者が圧倒的に大きいと思います。

プロセスをまたがったり、スレッドをまたがったりするときは
常に安全・安定な方向になるよう、気をつけないといけません。

> また、XMLファイルが作成途中(例えば大容量のファイルで、監視フォルダへのコピーに時間がかかっている状態)ではないことの判断は、
> 拡張子がaaa(仮)の同名ファイルが同じフォルダに存在するかどうかで判断します。

これはダメな方向であろうと思います。
ユーザーがaaaを作ってしまったらシステムが止まります。
また、XMLファイルを作成するプロセスが途中で殺されて、aaaが残ってしまっても同様です。
ファイルシステムで何かを保証しようと思ったら、
ファイルの存在ではダメで、ファイルのロックを使うべきです。

大体概要もわかったので、より詳細に私のお勧めを書いておきます。

OnStartか何かでワーカースレッド作成。
OnPauseやOnStopでは終了フラグや停止フラグを設定。
ワーカースレッドは無限ループ。
ループ内でSleep(1000)。
Sleepから戻ったらまず終了フラグや停止フラグの確認。
次に時刻の確認。
時刻が設定より過ぎていれば処理。そうでなければまたSleep。
処理を行うファイルは「排他」ロックをかける。「共有」ロックではなく。
排他ロックが取得できなければそのファイルは飛ばす。
DBに登録。その際、2重に登録されているなら飛ばす。
処理が終わったら、排他ロック解除後バックアップフォルダに「移動」。
移動が失敗した場合はあきらめる。

あまり真剣に考えてませんが、
これでたぶん安全に処理ができると思います。
どこで例外がおきても、どこでプロセスが殺されても、
大丈夫なように考えましょう。
引用返信 編集キー/
■9714 / inTopicNo.22)  Re[11]: System.Timers.Timerでのイベント処理
□投稿者/ nbmyou (52回)-(2007/11/02(Fri) 16:58:14)
No9706 (れい さん) に返信

詳細な回答、ありがとうございます。

>>また、XMLファイルが作成途中(例えば大容量のファイルで、監視フォルダへのコピーに時間がかかっている状態)ではないことの判断は、
>>拡張子がaaa(仮)の同名ファイルが同じフォルダに存在するかどうかで判断します。
>
> これはダメな方向であろうと思います。
> ユーザーがaaaを作ってしまったらシステムが止まります。
> また、XMLファイルを作成するプロセスが途中で殺されて、aaaが残ってしまっても同様です。
> ファイルシステムで何かを保証しようと思ったら、
> ファイルの存在ではダメで、ファイルのロックを使うべきです。

XMLファイルを監視フォルダに作成する処理を行うのは、
他者が作成したプログラムです。こちらの実装を変えることはもうできないのですが、
何かいい方法は考えられますでしょうか。

なお、XMLの読み込みですが、現在XMLSerializerを使用しています。
XMLと同じ構成のクラスを作成するだけでいいので、簡単だったもので・・・。
(出力も入力も自分でやるのでなければ、XMLSerializerは良くないと、当サイトの別の質問で言われましたが、今のところ使い続けています)
ここで読み取りに失敗すると例外の内容に関わらず10回までリトライし、あきらめるつくりにしています。


> 大体概要もわかったので、より詳細に私のお勧めを書いておきます。
どうもありがとうございます。
経験者の方の考えが聞けると、とてもいい経験になります。


> OnPauseやOnStopでは終了フラグや停止フラグを設定。
終了フラグ・停止フラグというのは、どういう事態を回避するためのものでしょうか。
(サービスが止まっても、ワーカスレッドは動いているということ?)


> 次に時刻の確認。
> 時刻が設定より過ぎていれば処理。そうでなければまたSleep。
時刻の確認とは、Windowsの時計を確認するということでしょうか。
例えばポーリング間隔が5分で、サービス開始時の時刻が17:00であった場合、
設定時刻を17:05分とし、Sleepから復帰する毎にWindowsの時計と設定時刻を見比べる・・・
という認識で合ってますでしょうか。
上記のような処理であれば、WIndowsの時刻が修正された場合のことを考えなければならなくなりそうですよね。


> 処理を行うファイルは「排他」ロックをかける。「共有」ロックではなく。
すみません。Googleで検索してみましたが、
C#でファイルのロックを取得する方法が見つけられません。
ヒントでも構いませんので、教えていただけませんでしょうか。

たびたび申し訳ありませんが、よろしくお願いいたします。
引用返信 編集キー/
■9720 / inTopicNo.23)  Re[12]: System.Timers.Timerでのイベント処理
□投稿者/ れい (163回)-(2007/11/02(Fri) 18:01:19)
No9714 (nbmyou さん) に返信
> ■No9706 (れい さん) に返信
> >>また、XMLファイルが作成途中(例えば大容量のファイルで、監視フォルダへのコピーに時間がかかっている状態)ではないことの判断は、
> >>拡張子がaaa(仮)の同名ファイルが同じフォルダに存在するかどうかで判断します。
>>
>>これはダメな方向であろうと思います。
>
> XMLファイルを監視フォルダに作成する処理を行うのは、
> 他者が作成したプログラムです。こちらの実装を変えることはもうできないのですが、
> 何かいい方法は考えられますでしょうか。

無視すればいいだけですよね?
ファイルのロックがどうなってるのか、よく考えて見ましょう。

>>OnPauseやOnStopでは終了フラグや停止フラグを設定。
> 終了フラグ・停止フラグというのは、どういう事態を回避するためのものでしょうか。
> (サービスが止まっても、ワーカスレッドは動いているということ?)

サービスが止まるときにはワーカースレッドは止まるべきだというだけです。
pauseやstopをサポートしないつもりなら別に要りません。
最低でもstopぐらいサポートしておいたほうがいいと思いますが。

>>次に時刻の確認。
>>時刻が設定より過ぎていれば処理。そうでなければまたSleep。
> 上記のような処理であれば、WIndowsの時刻が修正された場合のことを考えなければならなくなりそうですよね。

もちろんそうです。
時刻が変更されたとき1回だけは
5分が3分になったり10分になったりしても問題ないのではないですか?
なら、簡単に解決できますよね。

>>処理を行うファイルは「排他」ロックをかける。「共有」ロックではなく。
> すみません。Googleで検索してみましたが、
> C#でファイルのロックを取得する方法が見つけられません。

普通、ファイルは開いたらロックされます。
.Netではいろいろ単純化されてますがFileShare構造体である程度制御できます。
FileStreamのFileShareパラメーターで何も指定しなければ、
他のプロセスは読むことも書くこともできません。
必要に応じてうまくロックすることです。
引用返信 編集キー/
■9721 / inTopicNo.24)  Re[12]: System.Timers.Timerでのイベント処理
□投稿者/ 魔界の仮面弁士 (515回)-(2007/11/02(Fri) 18:06:39)
No9714 (nbmyou さん) に返信
> 上記のような処理であれば、WIndowsの時刻が修正された場合のことを考えなければならなくなりそうですよね。
DateTime.Now であれば、そうなりますね。変更通知は、TimeChanged イベントで得られますが、
今回の場合は、System.Diagnostics.Stopwatch を使うという手もあるかもしれません。
動作モードが IsHighResolution = true であれば、システムタイマの影響を受けないようなので。

>>処理を行うファイルは「排他」ロックをかける。「共有」ロックではなく。
> C#でファイルのロックを取得する方法が見つけられません。
「ロックを取得する方法」というのは、『ファイルをロックする方法』という意味ですか?
それとも『ファイルがロックされているか判断する方法』という意味ですか?

前者の意味であれば、System.IO.FileStream のコンストラクタなどで。
後者の意味であれば、ファイル入出力処理時の例外を拾うことで。
引用返信 編集キー/

<前の20件
トピック内ページ移動 / << 0 | 1 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -