我在.net 2.0,C#中创建了一个web服务。每当Web服务客户端调用不同的方法时,我都需要将一些信息记录到文件中。
当一个用户进程正在写入文件而另一个进程尝试写入该文件时,就会出现问题。我收到以下错误:
该进程无法访问该文件,因为该文件正由另一个进程使用。
我尝试在C#中实现并且失败的解决方案如下所示。
我想知道这个问题是否存在完美而完整的解决方案?
答案 0 :(得分:10)
锁定可能失败,因为您的Web服务由多个工作进程运行。
您可以使用在进程间共享的已命名互斥锁来保护访问权限,这与使用lock(someobject) {...}
获得的锁不同:
Mutex lock = new Mutex("mymutex", false);
lock.WaitOne();
// access file
lock.ReleaseMutex();
答案 1 :(得分:1)
您没有说明您的网络服务是如何托管的,因此我认为它位于IIS中。除非您的服务在多个应用程序池中运行,否则我认为该文件不应被多个进程访问。不过,我猜你可能会在一个进程中的多个线程试图写入时出现此错误。
我想我会选择你自己建议的解决方案,Pradeep,构建一个对象来完成对日志文件的所有写入。在该对象内部,我有一个Queue,所有要记录的数据都写入其中。我有一个单独的线程读取此队列并写入日志文件。在像IIS这样的线程池托管环境中,创建另一个线程似乎不太好,但它只有一个......请记住,内存中的队列不会在IIS重置之后存在;当IIS进程出现故障时,您可能会丢失一些“正在进行中”的条目。
其他替代方案当然包括使用单独的进程(例如服务)来写入文件,但这会产生额外的部署开销和IPC成本。如果这对您不起作用,请使用单身人士。
答案 2 :(得分:0)
也许写一行“队列行”来写入文件,所以当你尝试写入文件时,它会不断检查文件是否被锁定,如果是 - 它会一直等待,如果它不是'锁定 - 然后写入。
答案 3 :(得分:0)
您可以将结果推送到MSMQ队列,并让Windows服务从队列中挑选项目并记录它们。它有点重,但它应该有用。
答案 4 :(得分:0)
Joel:当你说“队列行”时你的意思是创建一个在循环中运行的单独线程来继续检查队列以及在未锁定时写入文件吗?
Charles:我知道MSMQ和Windows服务的组合,但就像我说的,除了从Web服务中写入文件之外别无选择:)
感谢 pradeep_tp
答案 5 :(得分:0)
到目前为止尝试的所有接近的问题是多线程可以输入代码。 这是多个线程尝试获取并使用文件处理程序 - 因此错误 - 您需要工作线程之外的单个线程来完成工作 - 单个文件句柄保持打开状态。
可能最容易做的事情是在Global.asax中启动应用程序期间创建一个线程,并监听同步的内存中队列(System.Collections.Generics.Queue)。让线程打开并拥有文件句柄的生命周期,只有该线程可以写入文件。
ASP中的客户端请求会暂时锁定队列,将新的日志消息推送到队列中,然后解锁。
记录器线程将定期轮询队列以获取新消息 - 当消息到达队列时,线程将读取并将数据分发到文件中。
答案 6 :(得分:0)
要知道我在我的代码中尝试做什么,以下是我在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);
}
}
当我在调试模式下运行此代码(模拟一个用户访问)时,我在队列出列的行中收到错误“stack overflow”。
注意:在上面的代码中,ErrorLogger是一个具有写入文件代码的类。 objMessage是一个携带日志消息的实体类。
答案 7 :(得分:0)
或者,您可能希望将错误记录到数据库中(如果您正在使用)
答案 8 :(得分:0)
KOTH,
我已经实现了Mutex锁,它已经消除了“堆栈溢出”错误。在我得出结论是否在所有情况下都能正常工作之前,我还需要进行负载测试。
我正在阅读其中一个网站上的Mutex objets,其中说Mutex会影响性能。我想知道通过Mutex锁定一件事。
假设User Process1正在写入文件,同时User Process2尝试写入同一文件。由于Process1已对代码块进行了锁定,因此Process2将继续尝试或者在第一次调试后自行消失。?
感谢 pradeep_tp
答案 9 :(得分:0)
它将一直等到互斥锁被释放....
答案 10 :(得分:0)
乔尔:当你说“排队”时你呢 意味着创建一个单独的线程 循环运行以继续检查 队列以及写入文件时 它没有被锁定?是的,这基本上就是我的想法。让另一个有一个while循环的线程,直到它可以访问该文件并保存,然后结束。
但是你必须以第一个开始寻找的线程首先获得访问权的方式来做。这就是我说队列的原因。