|
■No103927 (Tom さん) に返信 > ただ件数がいま50万行をこえており書き込みに数秒かかります。
1行2000文字超 × 50万行だと、最大で約10億文字(2GB弱)換算ですね?
> この50万件のデータを書き込む関数のあとに数十行のデータを書き込む関数を呼んでいるのです 同一ファイルへの追記 (上書きではない) と思ってよいでしょうか。
それぞれの関数が、同一の Stream インスタンスに対して書き込んでいるのか、 あるいはファイル名のみが共有されており、それぞれの関数内で 書き込んでは都度 Close している処理なのかでも話が変わってきそうですが。
> もしかして最後まで書き終わる前にプロセスおわっちゃう?なんて思ったのですが、 OS の Write Cache 書き込みの遅延が原因では無いでしょうか。
通常、StreamWriter.Flush() や Close() を呼んだだけでは、データは .NET のバッファから OS のバッファに移るだけで、そこから 物理ディスクへの書き込み完了については OS のタイミングに委ねられます。
まずは FileStream.Flush(true) を試してみてください。 これは FlushFileBuffers API に相当する機能で、OS に対して 「ディスク装置のキャッシュもフラッシュせよ」という指示を行うものです。
using (var fs = new FileStream(csvFileName, FileMode.Create, FileAccess.Write)) using (var st = new StreamWriter(fs, System.Text.Encoding.GetEncoding("Shift_JIS"))) { // // 元の書き込み処理をここに記述 // st.Flush(); // StreamWriter(アプリ層) のバッファを FileStream (OS層) へ fs.Flush(true); // OS のバッファを物理ディスクへ強制フラッシュ }
この時、Flush(true) の前に Flush() を先に呼ぶようにしないと、 StreamWriter 内に残っているデータが OS に渡されないのでご注意を。
なお、Close() メソッドは内部で Flush() を呼びますが、 これは Flush(true) の呼び出しを含みません。 そのため、Flush(true) は明示的な呼び出しが必要です。 https://learn.microsoft.com/ja-jp/dotnet/api/system.io.filestream.flush?WT.mc_id=DT-MVP-8907&view=netframework-4.8.1 https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs#L195
別案として…そもそも Write Cache を使わないという選択肢もあります。 FileOptions.WriteThrough を指定することで、Write Cache 無しで 直接ディスクに書き込める方法 (Unbuffered I/O) です。
ただし書き込んでいる間、システム全体のパフォーマンスが低下する可能性があります。 2GB 弱の書き込みにこれを使うと、数秒どころか分単位で待たされるかもしれません。
この機能はログやトランザクションなど、 データの整合性が速度よりも優先される場合に用いられるオプションです。
using (var fs = new FileStream(csvFileName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, FileOptions.WriteThrough)) using (var st = new StreamWriter(fs, System.Text.Encoding.GetEncoding("Shift_JIS"))) { // 書き込み処理 }
|