I have an .ASMX
API build using c#/asp.net/.net4.7.2
and it has been set to use 16 threads in IIS
. There are various modules that write (only writing, no reading) to multiple log files like so:
2022-06-01-Wed-_ModuleA.log
2022-06-01-Wed-_ModuleB.log
I was using the usual lock() method:
public class Audit {
public static object LogWriteLocker = new object();
public void LogEntry(string path, string logText) {
lock (LogWriteLocker) {
File.AppendAllText(path, logText);
}
}
}
This should not be happening, but I was seeing errors like:
The process cannot access the file 'D:\MySite\App_Data\2022-06-01-Wed-_ModuleA.log' because it is being used by another process.
So I am trying to figure our a workaround like below:
readonly private static ReaderWriterLockSlim _readWriteLock = new ReaderWriterLockSlim();
public static void WriteToFileThreadSafe(string path, string text) {
_readWriteLock.EnterWriteLock();
try {
File.AppendAllText(path, text);
}
catch (Exception exx) {
LogException(exx, "Error Writing to Log");
}
finally {
_readWriteLock.ExitWriteLock();
}
}
Sorry if there is TMI, but my questions are:
- What is wrong with my implementation of the
lock()
method? - Is
ReaderWriterLockSlim
implementation any better? I have not used this before. - One problem is using a single lock object for multiple files, where as I should have an array of lock objects/ReaderWriterLockSlim. The number of log files are dynamic so how do I do that efficiently?
Thank you.
Edit:
Contemplating a 3rd option, that has separate locks for each file path:
private static ConcurrentDictionary<string, object> LogWriteLocker = new ConcurrentDictionary<string, object>();
public static void WriteToFileThreadSafe(string path, string text)
{
//Adds a key/value pair to the ConcurrentDictionary<TKey,TValue> if the key does not already exist. Returns the new value, or the existing value if the key already exists.
var lockObj = LogWriteLocker.GetOrAdd(path, new object());
lock(lockObj)
{
File.AppendAllText(path, text);
}
}