マルチスレッド環境でのファイル アクセス戦略 (Web アプリ)
-
09-06-2019 - |
質問
Web サービスから取得され、Web アプリケーション内でローカルにキャッシュされた一部のデータの XML 表現であるファイルがあります。考え方としては、このデータは とても 静的ですが、ただ かもしれない 変化。そこで、ファイルにキャッシュするように設定し、モニターをそれに当てて削除されたかどうかを確認しました。削除すると、ファイルはソースから更新され、再構築されます。
ただし、マルチスレッド環境では、ファイルの読み取り/書き込み中にデータにアクセスしようとするとエラーが発生するため、現在問題が発生しています。
ロック対象のオブジェクトを追加しましたが、これは読み取り/書き込み中に常にロックされるため、これは私を混乱させます。他のスレッドからアクセスしようとした場合、ロックが解除されるまで「待機」するよう指示されると理解していましたか?
言っておきますが、私はマルチスレッド開発にはまったくの初心者なので、これが私の失敗であることを喜んで受け入れます:)
- 何かが足りないのでしょうか?
- マルチスレッド環境における最適なファイル アクセス戦略は何ですか?
編集
申し訳ありませんが、これは使用していると言うべきでした ASP.NET 2.0 :)
解決
ファイルが別のプロセスによってロックされていないことを確認するために使用するコードは次のとおりです。100% 確実というわけではありませんが、ほとんどの場合、これで仕事は完了します。
/// <summary>
/// Blocks until the file is not locked any more.
/// </summary>
/// <param name="fullPath"></param>
bool WaitForFile(string fullPath)
{
int numTries = 0;
while (true)
{
++numTries;
try
{
// Attempt to open the file exclusively.
using (FileStream fs = new FileStream(fullPath,
FileMode.Open, FileAccess.ReadWrite,
FileShare.None, 100))
{
fs.ReadByte();
// If we got this far the file is ready
break;
}
}
catch (Exception ex)
{
Log.LogWarning(
"WaitForFile {0} failed to get an exclusive lock: {1}",
fullPath, ex.ToString());
if (numTries > 10)
{
Log.LogWarning(
"WaitForFile {0} giving up after 10 tries",
fullPath);
return false;
}
// Wait for the lock to be released
System.Threading.Thread.Sleep(500);
}
}
Log.LogTrace("WaitForFile {0} returning true after {1} tries",
fullPath, numTries);
return true;
}
もちろん、アプリケーションに合わせてタイムアウトと再試行を調整できます。書き込みに時間がかかる巨大な FTP ファイルを処理するためにこれを使用します。
他のヒント
として保存されているオブジェクトをロックオンしている場合、 静的 そうすれば、同じアプリケーション ドメイン内のすべてのスレッドに対してロックが機能するはずですが、問題のある行を確認できるようにコード サンプルをアップロードする必要があるかもしれません。
そうは言っても、IIS が次の環境で実行されるように構成されているかどうかを確認することも考えられます。 ウェブガーデン モード(すなわち、アプリケーションを実行する複数のプロセス)、ロック ロジックが壊れる可能性があります。このような状況はミューテックスを使用して修正できますが、単一プロセスで実行するようにアプリケーションを再構成する方が簡単ですが、Web ガーデンの設定をいじる前後のパフォーマンスをチェックするのが賢明です。パフォーマンス。
一時的な名前 (「data.xml_TMP」) でファイルを作成し、準備ができたら、その名前を本来の名前に変更することもできます。こうすることで、準備が完了するまで他のプロセスがアクセスすることはなくなります。
OK、私はこれに取り組んできて、基本的にいくつかのスレッドからコードからがらくたを取り除くためのストレステストモジュールを作成することになりました(関連する質問を参照).
この時点から、コード内の穴を見つけるのがはるかに簡単になりました。私のコードは実際にはそれほど遠くないことが判明しましたが、コードが入る可能性のある特定のロジックパスがあり、基本的に読み取り/書き込み操作がスタックする原因となっていました。つまり、時間内にクリアされなかった場合、ゴーブーム!
それを取り出してストレステストを再度実行したら、すべてうまくいきました。
ということで、特に何もしていないのですが、 特別 私のファイルアクセスコードでは、私が使用したことを確認しました lock
必要に応じてステートメントを追加します (つまり、読み書きするとき)。
使ってみてはどうでしょうか AutoResetEvent
スレッド間で通信するには?約 8 GB のファイルを作成するコンソール アプリを作成しました。 createfile
メソッドを作成し、そのファイルをコピーします main
方法
static AutoResetEvent waitHandle = new AutoResetEvent(false);
static string filePath=@"C:\Temp\test.txt";
static string fileCopyPath=@"C:\Temp\test-copy.txt";
static void Main(string[] args)
{
Console.WriteLine("in main method");
Console.WriteLine();
Thread thread = new Thread(createFile);
thread.Start();
Console.WriteLine("waiting for file to be processed ");
Console.WriteLine();
waitHandle.WaitOne();
Console.WriteLine();
File.Copy(filePath, fileCopyPath);
Console.WriteLine("file copied ");
}
static void createFile()
{
FileStream fs= File.Create(filePath);
Console.WriteLine("start processing a file "+DateTime.Now);
Console.WriteLine();
using (StreamWriter sw = new StreamWriter(fs))
{
for (long i = 0; i < 300000000; i++)
{
sw.WriteLine("The value of i is " + i);
}
}
Console.WriteLine("file processed " + DateTime.Now);
Console.WriteLine();
waitHandle.Set();
}