多线程环境下的文件访问策略(Web App)
-
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 是否配置为在 网络花园 模式(即超过 1 个进程在执行您的应用程序),这会破坏您的锁定逻辑。虽然您可以使用互斥体来修复这种情况,但重新配置应用程序以在单个进程中执行会更容易,尽管您最好在弄乱网络花园设置之前和之后检查性能,因为它可能会影响表现。
您可以使用临时名称(“data.xml_TMP”)创建文件,并在准备好后将名称更改为应有的名称。这样,在它准备好之前,没有其他进程会访问它。
怎么样使用 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();
}