我有一个文件,该文件是从Web服务获取并在Web应用程序本地缓存的某些数据的XML表示。想法是该数据是非常静态的,但是可能会发生变化。因此,我将其设置为缓存到文件,并在其上贴上监视器以检查其是否已删除。删除后,文件将从其源刷新并重建。
我现在遇到了问题,因为很明显,在多线程环境中,当它仍在读取/写入文件时尝试访问数据时,它会崩溃。
这使我感到困惑,因为我添加了一个要锁定的对象,并且在读取/写入期间始终将其锁定。据我了解,尝试从其他线程进行的访问将被告知"等待",直到锁被释放?
让我知道,我是多线程开发的真正新手,所以我完全愿意接受这对我来说是一个麻烦:)
-
我想念什么吗?
-
在多线程环境中最佳的文件访问策略是什么?
编辑
对不起-我应该说这是使用ASP.NET 2.0:)
这是我用来确保文件未被另一个进程锁定的代码。这不是百分百的万无一失,但大多数情况下它都能完成工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| /// <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文件。
好吧,我一直在努力,最终创建了一个压力测试模块,基本上是从几个线程中剔除我的代码(参见相关问题)。
从现在开始,在我的代码中查找漏洞要容易得多。事实证明,我的代码实际上并不是遥不可及的,但是有一定的逻辑路径可以进入,这基本上导致了读/写操作的堆积,这意味着如果不及时清除它们,它将繁荣!
一旦我取出来,再次进行压力测试,一切正常!
因此,我并没有在文件访问代码中做任何特别的事情,只是确保在适当的地方(即读或写时)使用了lock语句。
您可以使用临时名称(" data.xml_TMP")创建文件,并在准备好名称后将其更改为应该使用的名称。这样,在准备就绪之前,没有其他进程可以访问它。
如果要锁定存储为静态对象的对象,则该锁应适用于同一应用程序域中的所有线程,但是也许您需要上载代码示例,以便我们查看有问题的行。铅>
也就是说,一种想法是检查IIS是否配置为以Web Garden模式运行(即,执行应用程序的进程超过1个),这会破坏锁定逻辑。尽管您可以使用互斥锁解决这种情况,但重新配置应用程序以在单个进程中执行将更容易,尽管明智的做法是在弄乱Web Garden设置之前和之后检查性能,因为它可能会影响性能。
如何使用AutoResetEvent在线程之间进行通信?我创建了一个控制台应用程序,该应用程序在createfile方法中创建了大约8 GB的文件,然后在main方法中复制了该文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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();
} |