none
File.ReadAllTextやWriteAllTextについて RRS feed

  • 質問

  • C#でASP.NETアプリを作っていて、テキストファイルの読み込み・書き込みにはFile.ReadAllTextやWriteAllTextを使っています。

    作っているプログラムは複数のプロセスから同時に一つのテキストファイルにアクセスするような動作もありうるのですが、
    File.ReadAllTextやWriteAllTextを使う場合、排他処理は必要でしょうか?

    また一般にC#()ではファイルの排他処理はどのようにするのがベストなのでしょうか?
    よろしくお願いします。

    2010年9月7日 6:19

回答

  • 排他は必要です。一般的には以下などが参考になると思います。

    ファイルの読込・書込操作で排他制御したい
    http://dobon.net/vb/bbs/log3-5/2797.html

    監視により作成/変更が通知されたファイルを開くには?
    http://www.atmarkit.co.jp/fdotnet/dotnettips/284watchopen/watchopen.html

    また、そのWebアプリケーションでしかそのファイルを扱わないのであれば、ファイル書き込み中などの情報をグローバル変数に持って判断する処理を最初に行い、その上で上記に掲載されている排他制御を行うとパフォーマンスが良いと思います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク 山本春海 2010年10月1日 4:48
    2010年9月7日 6:34
    モデレータ
  • 単純に排他するにしても、注意深く設計しないとクライアント側の待ちが長くなるかもしれません。
    (1 つのファイルに何十プロセスからのリクエストを処理することはないと思いますが、念のため)

    サーバー側のプログラムに対する要求と、サーバー側のプログラムとして何を保障しなければならないかなどを踏まえて、手法の選択・環境の整備・プログラムの構築などを進めていく必要があるように思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年10月1日 4:48
    2010年9月7日 14:10
    モデレータ

すべての返信

  • 排他は必要です。一般的には以下などが参考になると思います。

    ファイルの読込・書込操作で排他制御したい
    http://dobon.net/vb/bbs/log3-5/2797.html

    監視により作成/変更が通知されたファイルを開くには?
    http://www.atmarkit.co.jp/fdotnet/dotnettips/284watchopen/watchopen.html

    また、そのWebアプリケーションでしかそのファイルを扱わないのであれば、ファイル書き込み中などの情報をグローバル変数に持って判断する処理を最初に行い、その上で上記に掲載されている排他制御を行うとパフォーマンスが良いと思います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク 山本春海 2010年10月1日 4:48
    2010年9月7日 6:34
    モデレータ
  • >File.ReadAllTextやWriteAllTextを使う場合、排他処理は必要でしょうか?

    trapemiyaさんも仰っていますが、必要です。

     

    Webアプリケーションは、複数のユーザが同時にアクセスしてきます。

    よって、ファイル操作には複数プロセス間での排他制御をする必要があります。

     

    >また一般にC#()ではファイルの排他処理はどのようにするのがベストなのでしょうか?

    紹介されている内容とは別のものになりますが、ASP.NET(C#)には以下があります。

     

    Application.Lock();
    // この間はプロセス間で排他制御
    Application.UnLock();
    

     

    以下の参考のように利用することで排他処理になると思います。

    http://www.ken3.org/asp/backno/asp063.html

     

    2010年9月7日 7:15
  • >また、そのWebアプリケーションでしかそのファイルを扱わないのであれば、ファイル書き込み中などの情報をグローバル変数に持って判断する処理を最初に行い、その上で上記に掲載されている排他制御を行うとパフォーマンスが良いと思います。

    >紹介されている内容とは別のものになりますが、ASP.NET(C#)には以下があります。
    >…Application.Lock の話…

    ASP.NET環境においては、上記いずれのやり方でも排他は不完全になります。
    通常は、Mutexなどを使ってプロセス間も含めて排他制御する必要があります。
    どういうやり方がいい(あるいは必要)かは、ファイルへのアクセスをどのように行う(どういう風にファイルを使うのか)に依存しますので、一概には言えませんが。
    2010年9月7日 12:38
  • 単純に排他するにしても、注意深く設計しないとクライアント側の待ちが長くなるかもしれません。
    (1 つのファイルに何十プロセスからのリクエストを処理することはないと思いますが、念のため)

    サーバー側のプログラムに対する要求と、サーバー側のプログラムとして何を保障しなければならないかなどを踏まえて、手法の選択・環境の整備・プログラムの構築などを進めていく必要があるように思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年10月1日 4:48
    2010年9月7日 14:10
    モデレータ
  • システム要件がよくわからいので、外していたらごめんなさい。

     自分がasp.netなどで複数のアクセスが予想される場合は、テキストファイルではなくデータベースに格納して、データベースの排他制御に任せています。(SQL Server Express Editionなら無償で使えますし)

     あと仮に動的にhtmlを生成する場合、(MemoryStreamとか使えない場合)画像などは、GUIDを生成してテンポラリフォルダを作成して、そこに画像ファイルを保存するようにしています。
    (セッション終了の判断ができない場合、生成されたフォルダ/ファイルのゴミ掃除を考えなければいけませんが)

    案件や使用によりますので、一概にデータベースを使用する訳に行かない場合もあるかと思いますが、参考になれば幸いです。

    2010年9月7日 15:07
  • 皆様ありがとうございます。とても参考になりました。
    ぶっちゃけ今ASP.NETで作っているのは電子掲示板です。

    データ保存先がデータベースだと管理しやすいのかもしれませんが、
    安いデータベース無しのレンタルサーバを使うのでテキストデータでいきたいと思ってます・・。

    Mutexについて、使ったことがないので勉強したいと思います。

    Application.Lock()、Unlock()は私にとって簡単なので最も魅力的な方法です。
    不完全では存在している意味が無いような気もしますが、やはり不完全なのでしょうか?
    これらにはどのような問題があるのでしょうか?

    どうかよろしくお願いいたします。

    2010年9月8日 3:12
  • Mutex にすべきかどうかは、対象のファイルを ASP.NET の Web アプリ以外でも読み書きするかどうかによります。
    数のプロセスから同時に一つのテキストファイルにアクセスする

    と元々の質問にありましたので、ASP.NET 以外のプロセスからも読み書きするのであれば、Mutex という選択肢も入ってくるということだと思います。
    電子掲示板ならば、きっと対象の Web アプリからしか読み書きしないと思いますので、Application.Lock() でも問題ないように思います。

    Mutex はカーネル レベルに処理が落ちますので、パフォーマンスが大きく違ってきます。

    でも、電子掲示板を作る際に ReadAllText とか WriteAllText とかしてしまって大丈夫なのでしょうか。
    ファイルのサイズが大きくなるにつれて、加速度的にパフォーマンスが落ちてしまうように思うのですが。
    2010年9月8日 3:33
  • Mutex にすべきかどうかは、対象のファイルを ASP.NET の Web アプリ以外でも読み書きするかどうかによります。

    いえ、そうではなく、ASP.NET内でのアクセスだけの場合でも、本当はプロセス間まで意識した排他制御をする必要があります。グローバル変数も、Application.Lokも、あるいはMonitorによるlockでも、いずれもスコープはアプリケーションドメインに閉じていますので、このスコープだけでは本当は不足です。

    なぜか不足かというと、ASP.NETでは、一つのアプリケーションが同時に一つのアプリケーションドメインでしか動作しないという保証はないからです。

    あまりないとは思いますが、Webガーデンを有効にすれば複数のワーカープロセスが単一のWebアプリケーションを同時に実行しますし、アプリケーションドメインやワーカープロセスなどのリサイクルのタイミングでは、古い環境と新しい環境がオーバーラップして実行されるタイミングが発生することがあります。

    Mutex はカーネル レベルに処理が落ちますので、パフォーマンスが大きく違ってきます。

    Mutexの負荷が、例えばApplication.LockやMonitorでのlockなどと比較すると、特定条件においては相対的にかなり大きいのは事実ですが、どの程度影響があるかは状況によります。

    今回はファイルへの読み書きを、Open、Closeまで含めて実行するのですから、もともとカーネル呼び出しが何度も行われています。ファイルの読み書き自体も、実質的にはキャッシュになるでしょうが、それなりに時間はかかります(ファイルサイズがが非常に小さいなら別ですが)。

    1度や2度カーネル呼び出しが増えたところで大差は出ないでしょうし、そもそもApplication.Lockであっても待機が発生すれば結局カーネルモードに移行します。差が大きいのは、競合が少なく、めったに待機が発生しない場合です。

    2010年9月8日 12:32
  • 実際にレンタルサーバで使えるのかなどを含め、私自身が使ったことないので分かりませんが、例えばSQL Liteなどの、ファイルベースで利用できるDBなら使える可能性もあるかもしれません。

     

    2010年9月8日 12:46
  • 直接の回答ではありませんが・・・

    > ぶっちゃけ今ASP.NETで作っているのは電子掲示板です。

    書き込みだけは匿名ユーザーに許すとして、更新や削除まで匿名ユー
    ザーに勝手にされては困るはずで、どうしてもユーザー認証・承認の
    仕組みは必要だと思いますが。

    そうであれば、認証システムの選択は、たぶん、ASP.NET Forms 認証
    となって、DB は使わざるを得ないということになりませんか?

    認証・承認システムも、ファイルベースで、自力でコードを書いて実
    装するつもりであれば、話は別ですが。

    > データ保存先がデータベースだと管理しやすいのかもしれませんが、
    > 安いデータベース無しのレンタルサーバを使うのでテキストデータ
    > でいきたいと思ってます・・。

    SQL Server 付でも十分安いレンタルサーバーはあります。というより、
    ASP.NET が使えるホスティングサービスで DB サーバーなしというの
    はありますか?

    2010年9月8日 13:35
  • 「File.ReadAllTextやWriteAllTextを使う場合、排他処理は必要でしょうか?」という質問の仕方が気になりました。
    みなさんの回答は一貫して「必要です」ですが、それはFile.ReadAllTextやWriteAllTextが機能的に貧弱だから必要なわけではありません。

    例えば、AさんBさんがほぼ同時に書き込みを行ったとします。書き込み数の上限に達していないか、スレッド式なら親コメントが削除されていないか等、なんらか読み込み処理が必要なはずです。そこで例えばこんな順で処理が実行されたとすると

    1. Aさんの書き込み前の読み込み+チェック処理
    2. Bさんの書き込み前の読み込み+チェック処理
    3. Aさんの書き込み処理
    4. Bさんの書き込み処理

    考えるとすぐにわかると思いますが、Aさんの書き込みは消えてしまいます。みなさんの回答は「こういったことが発生しないようにするために、たぶん排他処理が必要になるだろう」です。そしてどんな時に排他処理を行わなければならないのかは、princekunさん自身が検討し決定する必要があります。

    その上で、上記例もそうですが、データをファイルに保存するとのことですので、ファイルにまつわる排他が必要な個所が出てくると思いますが、その際には、File.ReadAllTextやWriteAllTextを使うのではなく、FileAccess引数付きのFileStreamコンストラクタ の利用をお勧めします。
    # これは一番最初のtrapemiyaさんの回答でもあります。

    理由は簡単で、ファイルにまつわることなので、ファイルで処理するのが効率いいからです。ロック対象は当然ながら当該ファイルなので、みなさんの回答で触れられていることが一気に解決します。Webガーデンがどうこう、カーネル呼び出しがどうこう、Application.Lock()が不完全かどうかといったことに悩まされずに済みます。

    この回答はあくまで、ファイルをロックする際の手段です。別のもの、例えばApplication(HttpApplicationState)をロックするにはApplication.Lock()が適切です。
    結局、まず必要なのは「なんとなく排他をしたい」ではなく「どんなときにどの処理とどの処理を排他する/同時実行を許す」をprincekunさんが決定することが先決です。

    2010年9月8日 15:36
  • その上で、上記例もそうですが、データをファイルに保存するとのことですので、ファイルにまつわる排他が必要な個所が出てくると思いますが、その際には、File.ReadAllTextやWriteAllTextを使うのではなく、FileAccess引数付きのFileStreamコンストラクタ の利用をお勧めします。
    # これは一番最初のtrapemiyaさんの回答でもあります。

    理由は簡単で、ファイルにまつわることなので、ファイルで処理するのが効率いいからです。ロック対象は当然ながら当該ファイルなので、みなさんの回答で触れられていることが一気に解決します。Webガーデンがどうこう、カーネル呼び出しがどうこう、Application.Lock()が不完全かどうかといったことに悩まされずに済みます。

    いろいろな理由から、File.ReadAllTextやWriteAllTextよりもFileStreamを使う方が望ましいというのはその通りですが、これでロックしたところでWebアプリのような処理の制御では、ほとんどの場合これだけでは無理があるでしょう。 

    ファイルの機能そのものでのロックはあくまで排他だけであって、書き込み処理完了を待ったりなどの柔軟な制御はできず、例外になったら少し待ってリトライなどしかできませんので、結局どうしても別の手段で制御が必要になります。

    2010年9月8日 15:47
  • 完全に話題が本題からそれてしまいますが…

    技術的なフォーラムですから技術的な話に終始してしまいますが、作成するのが電子掲示板ということで、マイクロソフトのBPOS(Business Productivity Online Suite、URIはhttp://www.microsoft.com/japan/online/default.mspx)を契約して、SharePointでカスタマイズした方がよかったりして…。
    (完全にぶっちゃけすぎているのは自覚してます。あと案件に適合するかどうかも無視しています、はい。)

    2010年9月8日 18:10
  • ファイルの機能そのものでのロックはあくまで排他だけであって、書き込み処理完了を待ったりなどの柔軟な制御はできず、例外になったら少し待ってリトライなどしかできませんので、結局どうしても別の手段で制御が必要になります。

    確かに排他であって同期でない点は気になっています。

    しかし、だからと言ってファイルと異なるロック範囲を持つ排他機能を使っては本末転倒です。
    Application.Lock()が不完全でMutexならばと提案されていますが、Mutexを取得しないメモ帳などで操作されたら終わりですし、複数Webサーバーでファイル共有経由などになったらMutexは効きません。
    # と言いつつファイル共有でFileShareは効くんでしたっけ f(^^;
    # もちろんその規模ならファイルでなくデータベースを使うべきですけどね。

     

    ところで、FileAccess付きと書きましたが、FileShare付きの間違いでした orz

    2010年9月9日 0:51
  • 確かに排他であって同期でない点は気になっています。

    しかし、だからと言ってファイルと異なるロック範囲を持つ排他機能を使っては本末転倒です。
    Application.Lock()が不完全でMutexならばと提案されていますが、Mutexを取得しないメモ帳などで操作されたら終わりですし、複数Webサーバーでファイル共有経由などになったらMutexは効きません。
    # と言いつつファイル共有でFileShareは効くんでしたっけ f(^^;
    # もちろんその規模ならファイルでなくデータベースを使うべきですけどね。

    もちろん基本的にはWebアプリからしか操作しない前提です。 ファイルロックも必要なら併用すればいいだけの話です。ファイルロックを使うなと言っているわけではありません。
    複数Webサーバとかになればもちろん別の手段が必要です。普通は素直にDBなどにするでしょうけれど。

    ※というか、別にMutexなら完璧だとか言っているわけじゃありません。それくらいの制御は通常必要になるだろうといっているだけです。

    Webアプリケーションで、ファイルロックだけで適切な処理ができるなら、参考にどのようにするのか教えてください。
    ファイルアクセスの頻度が低いならそれでも許容範囲でしょうが、まともにアクセスするならちょっと無理があるように思いますが。

    2010年9月9日 1:45
  • もう一点。

    しかし、だからと言ってファイルと異なるロック範囲を持つ排他機能を使っては本末転倒です。
    Application.Lock()が不完全でMutexならばと提案されていますが、Mutexを取得しないメモ帳などで操作されたら終わりですし、複数Webサーバーでファイル共有経由などになったらMutexは効きません。


    ファイルロックしたところでメモ帳などで操作されたらどうにもなりません。もし編集中のファイルをロックしたままの仕様のアプリで開いてしまえばサービス停止に陥りますし、読み書き時のみロックなら、読み込んだ時点以降の更新が上書きされてしまいます。 ファイルロック以外ではもちろん操作を防げませんが、ファイルロックでも実質大して変わりません。

    本末転倒とのことですが、ファイルロックで排他することにこだわって、Webアプリケーション本来の性能やレスポンスを損なったり、余計な負荷をかけたりしては、そちらの方が本末転倒です。ファイルロックでよいやり方があれば別なのですが。

    2010年9月9日 11:49
  • パフォーマンスや厳密な排他を意識するならDBなのでは? その上でテキストファイルに格納する方法について言及しているのでは。

    Application.Lock()が不完全でMutexを提示しておきながら、後出しで実はMutexは不完全だけどパフォーマンスを考慮したら不完全でも構わないと言っているように聞こえます。その基準がよくわかりません。

    私としてはテキストファイルと妥協している時点で、FileShare+リトライの効率の悪い待機方法でもそれはそれでありかなと思います。それに満足できず待機可能なLock方法を提示してほしいということなら、私自身試したことがないのでどこまで使えるかわからない+.NET Framework外となりますが、LockFileEx() とか。これはFileStream.Lock() の待機バージョンです。

    2010年9月9日 12:30
  • パフォーマンスや厳密な排他を意識するならDBなのでは? その上でテキストファイルに格納する方法について言及しているのでは。

    テキストファイルを扱う上では、このあたりに気を使う必要があるだろう、こういう問題が発生する可能性があるだろう、という観点で書いています。

    もちろん、DBが使えるならその方がよいと思いますが、たまたまファイルアクセスの話をしていたので、それに関して気になったことなどを書いているだけです。

    それとも、テキストファイルなのだからパフォーマンスやその他気にしなくていいということですか?
    例えばログをファイルに書きだしたりとか(まあイベントログ使えとかそれはそれでいろいろ話はあるでしょうが)、その場合にパフォーマンスや排他を、できる範囲で(あまり無理がない範囲、あまり大きな労力をかけない範囲で、なるべく望ましくなるように)意識しませんか? 

    Application.Lock()が不完全でMutexを提示しておきながら、後出しで実はMutexは不完全だけどパフォーマンスを考慮したら不完全でも構わないと言っているように聞こえます。その基準がよくわかりません。

    ファイルアクセス前提なら、どちらかといえばサーバは一台の可能性が高いでしょう(どちらかといえば、ですが)。
    そのうえで、単一サーバ内での排他なら、Mutexなどで比較的容易にできますから、そこまではやった方がいいだろうという判断というか、そのあたりが基準です。 複数サーバならそもそもそんな単純な話ではすみませんから(複数サーバならそんな単純な話ではすまないというのは、私の感覚ではWebアプリでファイルロックだけで排他処理する、というのは正直選択肢になかったせいです)。

    そもそも、もともとのApplication.Lockやその他、アプリケーションドメイン内での排他でよいという回答があったから、それだけでは一般に不十分だよ、という指摘をしただけです。

    複数サーバならそれではだめなのは明らかでしょうが、単一サーバでもそれだけではダメということはわかりにくいでしょう。だから指摘しました(元の回答が単一サーバ前提の内容ですから)。まあ、複数サーバの場合にも触れておいた方が良かったかもしれませんが。

    私としてはテキストファイルと妥協している時点で、FileShare+リトライの効率の悪い待機方法でもそれはそれでありかなと思います。

    それはそれでありな場合もあると思いますが、かなりアクセス頻度が低い場合ではないですか?今回のように、メインの処理で使いそうな場合には、少なくともおすすめできるやり方とは思えません。

    最初から、妥協してこういうやり方もありだと思う、という書き方なら別に何も言いませんよ(あまりお勧めではない、くらいは言ったかもしれませんが)。 

    あなたは、ほかのやり方は本末転倒だといい、このやり方で一気に解決する、と言ったので、そうは思えないということを書いたまでです。

    それに満足できず待機可能なLock方法を提示してほしいということなら、私自身試したことがないのでどこまで使えるかわからない+.NET Framework外となりますが、LockFileEx() とか。これはFileStream.Lock() の待機バージョンです。

    方法としてはとてもよさそうですが、.NET Framework上ではMutex使うよりは難しいでしょう。
    いいとは思いますけどね(私も試したことないのでわかりませんが)。

    2010年9月9日 13:43
  • 他人を否定するときは極端な制約を付け、自分自身に対しては後付け都合のいい条件をつけるんですね。参考になりますw

    Mutexは作成・削除だけで満足してしまい、その後の待機をしていないコードをよく見かけるので気を付けてください。 > princekunさん

    「複数のプロセスから」という条件ですので、MutexSecurity付きのコンストラクタ を呼ぶ必要がありけっこう厄介です。蛇足ですが、このオーバーロードは.NET 1.1にはなく結局当時はCreateMutex() を呼ぶ必要がありました。

    2010年9月10日 11:01
  • 一応返信します。


    極端な制約とはWebアプリでパフォーマンスなどを気にすることですか?
    もしそうなら、私はそれを極端だとは思っていません、あなたは極端だと思うのでしょう。

    後付け都合のいい条件とは、複数Webサーバや他のアプリからの操作を想定していない点ですか?
    複数Webサーバの話なら、前にも書きましたが、最初に私が指摘した対象が元々単一Webサーバ前提の話だったからです。
    今となっては複数Webサーバの話も書いときゃよかったと思ってますが。
    それを後付けの都合のいい条件と言われるならどうしようもありません。

    他のアプリからの操作は確かに最初から想定していませんが、これを想定するならずっと話が難しくなります。
    ファイルロックを使う方法であってもダメなのは前にも書いた通りです。
    貴方も結局同じ前提で書いているのですから、そこを都合がいいと言われても困ります。

     

    一応元質問者さんへ

    Mutexの、MutexSecurity付きのコンストラクタの話についてですが、これもWebアプリからの操作のみという前提であれば問題ありません、少なくとも普通に使えます。
    ただし、Webアプリと、他の通常のアプリ間でMutexで制御したいのであれば意識する必要が(通常は)あります。

    なんかややこしそうなのでMutexは使いたくないということなら、Application.Lockや、通常のlockを使ってください(Application.Lockよりは、普通のlockをおすすめします)。
    この場合は、佐祐理さんが前に書かれていたように、排他が必要な処理の範囲で、必ずファイルのロックを維持し続けるように注意してください。
    その上で、ファイルオープンで例外になった場合は適度にリトライをかけるようにすれば、運が悪くとも、ファイルが壊れたり、いきなりエラー画面が返るという事態は避けられます。

    最初の質問であったように、ReadAllTextやWriteAllTextを使うやり方だと、運が悪いとファイルが壊れたり(更新が失われたり)する可能性があるので注意してください。

    念のためですが、この方法(Mutexを使わない方法)の場合はWebガーデンは避けてください(多分あまり使われないと思いますので大丈夫だとは思いますが)。
    Webガーデンだと普通に複数プロセスが同時に動くので、上記競合が頻繁に発生する可能性があります。

    ただ、ファイルを実際にどのように使っているのか(使うつもりなのか)わかりませんので、もし競合が発生する頻度が元々低いということなら、単にファイルロックを維持して必要に応じてリトライする方法をとるだけでも、そんなに問題はないでしょう。

    2010年9月10日 13:07
  • ありがとうございました。

    今回はLockと例外処理を合わせてを使おうと思います。

    ファイルロックは基本的ですが奥が深い話で勉強になりました。

    2010年9月11日 1:58