在.NET中的Web服务中写入单个文件的问题

我在.net 2.0,C#中创建了一个web服务。 每当Web服务客户端调用不同的方法时,我都需要将一些信息记录到文件中。

当一个用户进程正在写入文件而另一个进程尝试写入该文件时,就会出现问题。 我收到以下错误:

该进程无法访问该文件,因为该文件正由另一个进程使用。

我试图在C#中实现并且失败的解决方案如下。

  1. 实现包含写入文件的代码的单例类。
  2. 使用lock语句来包装写入文件的代码。
  3. 我也试过使用开源记录器log4net,但它也不是一个完美的解决方案。
  4. 我知道记录到系统事件记录器,但我没有这个选择。

我想知道这个问题是否存在完美而完整的解决方案?

锁定可能会失败,因为您的Web服务由多个工作进程运行。 您可以使用命名的互斥锁保护访问,该互斥锁在进程间共享,与使用lock(someobject) {...}获得的锁不同:

 Mutex lock = new Mutex("mymutex", false); lock.WaitOne(); // access file lock.ReleaseMutex(); 

您没有说明您的Web服务是如何托管的,所以我假设它在IIS中。 除非您的服务在多个应用程序池中运行,否则我认为该文件不应被多个进程访问。 不过,我猜你可能会在一个进程中的多个线程尝试写入时出现此错误。

我想我会选择你自己建议的解决方案,Pradeep,构建一个对象来完成对日志文件的所有写入。 在那个对象里面,我有一个Queue,所有要记录的数据都写入其中。 我有一个单独的线程从这个队列读取并写入日志文件。 在像IIS这样的线程池托管环境中,创建另一个线程似乎并不太好,但它只是一个…请记住,内存中的队列不会在IIS重置后继续存在; 当IIS进程关闭时,您可能会丢失一些“正在运行”的条目。

其他替代方案当然包括使用单独的进程(例如服务)来写入文件,但这会产生额外的部署开销和IPC成本。 如果这对您不起作用,请使用单身人士。

也许写一个“队列行”来写入文件,所以当你尝试写入文件时,它会不断检查文件是否被锁定,如果是 – 它会一直等待,如果它没有被锁定 – 然后写信给它。

您可以将结果推送到MSMQ队列,并让Windows服务从队列中挑选项目并记录它们。 它有点重,但它应该工作。

乔尔和查尔斯。 那很快! 🙂

Joel:当你说“队列行”时你的意思是创建一个在循环中运行的单独线程来继续检查队列以及在未锁定时写入文件吗?

Charles:我知道MSMQ和Windows服务组合,但就像我说的,除了从Web服务中写入文件之外别无选择:)

谢谢pradeep_tp

到目前为止尝试的所有接近的问题是多个线程可以输入代码。 这是multithreading尝试获取和使用文件处理程序 – 因此错误 – 您需要工作线程之外的单个线程来完成工作 – 单个文件句柄保持打开状态。

可能最容易做的事情是在Global.asax中的应用程序启动期间创建一个线程,并监听同步的内存中队列(System.Collections.Generics.Queue)。 让线程打开并拥有文件句柄的生命周期,只有该线程可以写入文件。

ASP中的客户端请求将暂时锁定队列,将新的日志消息推送到队列,然后解锁。

记录器线程将定期轮询队列以获取新消息 – 当消息到达队列时,线程将读取并将数据分发到文件中。

要知道我在我的代码中尝试做什么,以下是我在C#中实现的单线类

public sealed class FileWriteTest {

 private static volatile FileWriteTest instance; private static object syncRoot = new Object(); private static Queue logMessages = new Queue(); private static ErrorLogger oNetLogger = new ErrorLogger(); private FileWriteTest() { } public static FileWriteTest Instance { get { if (instance == null) { lock (syncRoot) { if (instance == null) { instance = new FileWriteTest(); Thread MyThread = new Thread(new ThreadStart(StartCollectingLogs)); MyThread.Start(); } } } return instance; } } private static void StartCollectingLogs() { //Infinite loop while (true) { cdoLogMessage objMessage = new cdoLogMessage(); if (logMessages.Count != 0) { objMessage = (cdoLogMessage)logMessages.Dequeue(); oNetLogger.WriteLog(objMessage.LogText, objMessage.SeverityLevel); } } } public void WriteLog(string logText, SeverityLevel errorSeverity) { cdoLogMessage objMessage = new cdoLogMessage(); objMessage.LogText = logText; objMessage.SeverityLevel = errorSeverity; logMessages.Enqueue(objMessage); } 

}

当我在调试模式下运行此代码(模拟一个用户访问)时,我在队列出列的行中得到错误“堆栈溢出”。

注意:在上面的代码中,ErrorLogger是一个具有写入文件代码的类。 objMessage是一个携带日志消息的实体类。

或者,您可能希望将错误记录到数据库中(如果您正在使用)

KOTH,

我已经实现了Mutex锁,它已经消除了“堆栈溢出”错误。 在我得出结论是否在所有情况下都能正常工作之前,我还要进行负载测试。

我正在阅读其中一个网站上的Mutex objets,其中说Mutex会影响性能。 我想通过Mutex锁定一件事。

假设User Process1正在写入文件,同时User Process2尝试写入同一文件。 由于Process1已对代码块进行了锁定,因此Process2将继续尝试,或者在第一次调试后自行消失。

谢谢pradeep_tp

它将一直等到互斥锁被释放….

Joel:当你说“队列行”时你的意思是创建一个在循环中运行的单独线程来继续检查队列以及在未锁定时写入文件吗?

是的,这基本上就是我的想法。 让另一个有一个while循环的线程,直到它可以访问该文件并保存,然后结束。

但是你必须以第一个开始寻找的线程首先获得访问权的方式来做。 这就是我说排队的原因。