尝试删除文件时如何调试“共享冲突”

我有一个multithreadingC#应用程序,它创建文件,打开它们进行处理,然后在完成后删除它们。 此应用程序可以处理1到100个文件。 有些随机(很可能归因于应用程序的multithreading特性)当我尝试在处理后删除文件时,我收到了共享冲突。 我的直觉说,维克,在尝试删除文件之前,你没有正确关闭/处理文件。 如果它发生在每个文件中,我会尽我所能,但事实并非如此。 所以,我试图找出我犯了错误的地方。 那里的任何人都有关于如何调试这种类型的exception的指示? 如果有意义,我很乐意在文件上看到堆栈跟踪。

我将尝试显示伪代码,但是,我的问题更多的是关于如何调试这种类型的exception:

应用事件:

操作开始 + =创建新处理器。

传输文件 + = Processor.ProcessFile并将新的Document对象添加到处理器的文档集合(作为路径,而不是文件)

操作完成 + = Processor.Aggregate files,创建包含文件内容的新文件。 完成此方法后,它将调用ProcessorFinished。

处理器事件:

处理器已完成 + = Application_CleanUpProcessor。 在这种方法中,我处理了处理器,而处理器又处理了一个删除文件的文档对象。

接受您需要快速validation是否花费更多时间来调试代码,或者编写好的测试来certificate您的代码是正常的,您想要的是一种快速的方法来certificate没有其他进程正在使用您的文件。 所以,让我们假设:

  • 您的文件位于本地磁盘(不是网络共享)上,并且
  • 您怀疑防病毒,Windows索引或其他东西正在锁定除您的代码之外的文件
  • 因此,在编写测试之前,您需要一种快速的方法来限定输入/输出

您希望能够运行您的程序,并查看该文件发生了什么,导致您遇到共享冲突。

我会这样做:

1.从sysinternals下载ProcMon(10秒)

Procmon是一个很好的工具,您可以按顺序过滤到您希望在所有流程中看到的内容。 链接到来自Microsoft的sysinternals的procmon

2.提取并运行Procmon,添加filter和高亮显示(30秒)

打开procmon,为“Path”添加filter“以”开头“

添加过滤器以procmon

现在为“结果”添加一个突出显示“是”“共享违规”

添加共享冲突过滤器

最后,运行程序直到出现exception,然后右键单击路径列中包含共享冲突的文件,并选择“包括’< filename here >’”以删除所有其他结果。 您现在可以看到导致您的exception的文件的所有活动……

Procmon显示谁锁定了该文件

如果你想熟悉procmon,这里是我用来伪造这一切的代码。 它有一个用于锁定文件的侧线程,以及一个然后尝试锁定文件的主线程。 只需创建一个C#控制台应用程序即可。 它看起来像这样:

这是我之前制作的一个 - 锁和锁再次成为罪魁祸首

因此,在不到2分钟的时间内,您可以看到它的代码是否有问题,或者其他什么。 我前几天使用它来确定我的Com组件实际上是使用备用文件流,因此在尝试使用网络驱动器时抛出exception。 没有多少unit testing会帮助我。

这是强制共享违规的测试代码:

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.IO; using System.Threading; internal class Program { private static int lockPoint = 0; private static void Main(string[] args) { const string testFile = @"H:\test\test.txt"; FileInfo testFileInfo = new FileInfo(testFile); if (!testFileInfo.Directory.Exists) { testFileInfo.Directory.Create(); } // Clear our example if (testFileInfo.Exists) { testFileInfo.Delete(); } // Create the test file using (FileStream fs = File.Create(testFile)) using (StreamWriter sw = new StreamWriter(fs)) { sw.WriteLine("test file content"); } Task iLockTheFileFirst = new Task(() => { using (FileStream fsThread = File.Open(testFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { Console.WriteLine("iLockTheFileFirst: I opened the file"); // Set lockPoint to 1 and let main try and open the file Interlocked.Exchange(ref lockPoint, 1); // Wait until the main thread sets lockPoint to 3 const int ifEqualTo3 = 3; const int replaceWith4 = 4; while (Interlocked.CompareExchange(ref lockPoint, replaceWith4, ifEqualTo3) != ifEqualTo3) { Console.WriteLine("iLockTheFileFirst: Waiting for main thread to let me finish"); Thread.Sleep(1000); } } Console.WriteLine("iLockTheFileFirst: I have closed the file"); }); // Start the thread and lock the file iLockTheFileFirst.Start(); // Now spin until the lockPoint becomes 1 const int ifEqualTo1 = 1; const int replaceWith2 = 2; // If lockPoint is equal to 1 (ie the main thread wants us to finish), then move it to 2 while (Interlocked.CompareExchange(ref lockPoint, replaceWith2, ifEqualTo1) != ifEqualTo1) { Console.WriteLine("Main thread: waiting for iLockTheFileFirst to open the file"); Thread.Sleep(1000); } try { Console.WriteLine("Main thread: now I'll try opening the file"); using (FileStream fsMain = File.Open(testFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { Console.WriteLine("Main thread: I opened the file, which shouldn't be possible"); } } catch (IOException ioex) { Console.WriteLine("Main thread: IOException: " + ioex.Message); } catch (Exception ex) { Console.WriteLine("Main thread: some other exception: " + ex.Message); } // Set lockPoint to 3 and let other thread finish Interlocked.Exchange(ref lockPoint, 3); // Wait for other thread to finish const int ifEqualTo4 = 4; const int replaceWith5 = 5; while (Interlocked.CompareExchange(ref lockPoint, replaceWith5, ifEqualTo4) != ifEqualTo4) { Thread.Sleep(10); } Console.WriteLine("Main thread: Press enter to finish"); Console.ReadLine(); } } 

这就是所有人!