6

我有一个文件,它是一些数据的 XML 表示,这些数据取自 Web 服务并在 Web 应用程序中本地缓存。这个想法是这些数据是非常静态的,但可能会改变。因此,我将其设置为缓存到文件中,并在其上放置了一个监视器以检查它是否已被删除。删除后,文件将从其源刷新并重建。

不过,我现在遇到了问题,因为显然在多线程环境中,当它仍在读取/写入文件时尝试访问数据时,它会崩溃。

这让我很困惑,因为我添加了一个要锁定的对象,并且在读/写期间它总是被锁定。我的理解是,尝试从其他线程访问会被告知“等待”直到锁被释放?

只是让你知道,我是多线程开发的新手,所以我完全愿意接受这是我的一个错误:)

  • 我错过了什么吗?
  • 多线程环境中最好的文件访问策略是什么?

编辑

抱歉 - 我应该说这是使用ASP.NET 2.0 :)

4

5 回答 5

7

这是我用来确保文件未被另一个进程锁定的代码。它不是 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 文件。

于 2008-09-03T12:33:29.370 回答
1

如果您锁定存储为静态的对象,则该锁定应该适用于同一应用程序域中的所有线程,但也许您需要上传代码示例,以便我们查看有问题的行。

也就是说,一个想法是检查 IIS 是否配置为在Web Garden模式下运行(即,执行您的应用程序的多个进程),这会破坏您的锁定逻辑。虽然您可以使用互斥锁解决这种情况,但重新配置应用程序以在单个进程中执行会更容易,尽管您最好在弄乱网络花园设置之前和之后检查性能,因为它可能会影响表现。

于 2008-09-03T08:15:13.267 回答
1

您可以使用临时名称(“data.xml_TMP”)创建文件,并在准备好时将名称更改为应有的名称。这样,在它准备好之前,没有其他进程会访问它。

于 2008-09-03T09:17:52.583 回答
1

好的,我一直在研究这个并最终创建了一个压力测试模块,以基本上从多个线程中敲出我的代码中的废话(请参阅相关问题)。

从这一点开始,在我的代码中找到漏洞要容易得多。事实证明,我的代码实际上并不遥远,但是它可以进入某个逻辑路径,基本上导致读/写操作堆积,这意味着如果它们没有及时清除,它会去繁荣!

一旦我把它拿出来,再次运行我的压力测试,一切都很好!

所以,我并没有真正在我的文件访问代码中做任何特别的事情,只是确保我lock在适当的地方使用了语句(即在读取或写入时)。

于 2008-09-22T07:41:44.750 回答
0

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();
    }
于 2015-09-21T17:36:58.957 回答