C#2.0で非同期ファイル読み取りを実行する方法は?
-
03-07-2019 - |
質問
サイズがギガバイトを超えるテキストファイルのすべての行をループする必要があるアプリケーションがあります。これらのファイルの一部には、数千または数億の行があります。
現在の(および同期)読み取りの例は、次のようになります...
using (FileStream stream = new FileStream(args[0], FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (StreamReader streamReader = new StreamReader(stream)) {
string line;
while (!string.IsNullOrEmpty(line = streamReader.ReadLine())) {
//do stuff with the line string...
}
}
}
.Net非同期IOストリーミングメソッドに関する資料を読んだことがありますが、この問題に関する2つの具体的な質問についていくつかお世話になりました。
まず、これらのファイルを非同期で読み取ることにより、通常は短いが長さが異なる(ファイル内の各行間に関係がない)行全体を必要とする場合、パフォーマンスが向上しますか?
第二に、上記のコードを非同期読み取りに変換するにはどうすればよいですか。それで、今と同様に各行を処理できますか?
解決
行に非同期を読み取らせる代わりに、ファイルに非同期を読み取らせることもできます。これは、単一のワーカーデリゲートの質問に含まれるすべてのコードを網羅しています。
static void Main(string[] args)
{
WorkerDelegate worker = new WorkerDelegate(Worker);
// Used for thread and result management.
List<IAsyncResult> results = new List<IAsyncResult>();
List<WaitHandle> waitHandles = new List<WaitHandle>();
foreach (string file in Directory.GetFiles(args[0], "*.txt"))
{
// Start a new thread.
IAsyncResult res = worker.BeginInvoke(file, null, null);
// Store the IAsyncResult for that thread.
results.Add(res);
// Store the wait handle.
waitHandles.Add(res.AsyncWaitHandle);
}
// Wait for all the threads to complete.
WaitHandle.WaitAll(waitHandles.ToArray(), -1, false); // for < .Net 2.0 SP1 Compatibility
// Gather all the results.
foreach (IAsyncResult res in results)
{
try
{
worker.EndInvoke(res);
// object result = worker.EndInvoke(res); // For a worker with a result.
}
catch (Exception ex)
{
// Something happened in the thread.
}
}
}
delegate void WorkerDelegate(string fileName);
static void Worker(string fileName)
{
// Your code.
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (StreamReader streamReader = new StreamReader(stream))
{
string line;
while (!string.IsNullOrEmpty(line = streamReader.ReadLine()))
{
//do stuff with the line string...
}
}
}
}
他のヒント
非同期パターンはBeginRead()/ EndRead()です。
ブーストを取得するかどうかは、読み取りを行っているときに他に何が起こっているかに大きく依存します。読み取りを待っている間にアプリでできることは他にありますか?そうでない場合、非同期に移行してもあまり役に立ちません...
非同期読み取りでは、各ブロックのヘッドシークが増えます。ファイルシステム上のファイルを適切にデフラグし、同期読み取りを使用すると、パフォーマンスが向上します。
既に指摘したように、ライン処理を他のスレッドにディスパッチすると、ブーストが得られます(特にマルチコアCPUの場合)
パフォーマンスが非常に重要な場合は、 FILE_FLAG_SEQUENTIAL_SCAN
の相互運用を調査することをお勧めします。詳細はこちら
パフォーマンスを改善するかどうかを確認するために、フラグをオンにしてファイルをスキャンする小さなC ++アプリを作成する方が良いでしょう。