.NET 2.0 : File.AppendAllText(...) - Thread safe implementation - c#

As an exercise in idle curiosity more than anything else, consider the following simple logging class:
internal static class Logging
{
private static object threadlock;
static Logging()
{
threadlock = new object();
}
internal static void WriteLog(string message)
{
try
{
lock (threadlock)
{
File.AppendAllText(#"C:\logfile.log", message);
}
}
catch
{
...handle logging errors...
}
}
}
Is the lock needed around File.AppendAllText(...) or is the method inherently thread-safe by its own implementation ?
Searching for information on this yields a lot of contradictory information, some say yes, some say no. MSDN says nothing.

File.AppendAllText is going to acquire an exclusive write-lock on the log file, which would cause any concurrent thread attempting to access the file to throw an exception. So yes, you need a static lock object to prevent multiple threads from trying to write to the log file at the same time and raising an IOException.
If this is going to be an issue, I'd really suggest logging to a database table which will do a better job of handling concurrent log writers.
Alternatively, you can use TextWriterTraceListener which is thread-safe (well, it's going to do the locking for you; I'd rather write as little of my own multithreaded code as possible).

Testing parallel writes shows that you would get a System.IO.IOException if you were to comment out your lock statement.
[Test]
public void Answer_Question()
{
var ex = Assert.Throws<AggregateException>(() => Parallel.Invoke(
() => Logging.WriteLog("abc"),
() => Logging.WriteLog("123")
));
// System.IO.IOException: The process cannot access the file 'C:\Logs\thread-safety-test.txt' because it is being used by another process.
Console.Write(ex);
}

It is thread safe in the sense that it opens the file with Read sharing, so assuming your filesystem honors file locks, only one thread will be allowed to write to the file at a time. Other threads may, however, get dirty reads if they are attempting to read the same file.

Related

How can I optimize read access?

I work on data that is mostly read and I want to perform these works as efficiently as possible, and I need to provide thread-safe access to it.
Any explanations on my problem would be welcome. Thanks
So the basics of creating a thread
//
Threat t = new Thread (My_Function);
// or
Thread t = new Thread (()=>
{
//your code here
});
t.start();
If you want to make the read thread safe you can use the "lock statement inside the thread on the resources that you want to ensure serial access
lock (Read_resource_object)
{
}
What lock does is, is the first time code runs over the lock statement it will "lock" the resource until the then of the curly braces. This does not prevent other code from accessing that object, rather, if any other code calls a lock on that resource, that code blocks until the current thread that locked it, unlocks it. Of course be very careful to make sure your don't get a thread lock,which usually occurs if inside your first lock somehow the flow of the code results in trying to lock the same code before it gets unlocked. Other than that I would recomend reading some tutorials on this because mutli threading and thread saftey are difficult and complicated!
Also lookup tasks as well they wrap threads and provide additional functionality.
I found this answer in C# 6 Cookbook
Use ReaderWriterLockSlim to give multiple-read/single-write access with the capacity to upgrade the lock from read to write. As an example, say a developer is starting a new project. Unfortunately, the project is understaffed, so the developer has to respond to tasks from many other individuals on the team. Each of the other team members will also ask the developer for status updates on their tasks, and some can even change the priority of the tasks the developer is assigned. The developer is assigned a task via the AddTask method. To protect the Developer Tasks collection we use a write lock on ReaderWriterLockSlim, calling EnterWrite Lock when adding the task to the DeveloperTasks collection and ExitWriteLock when the addition is complete:
public void AddTask(DeveloperTask newTask)
{
try
{
Lock.EnterWriteLock();
// if we already have this task (unique by name)
// then just accept the add as sometimes people
// give you the same task more than once :)
var taskQuery = from t in DeveloperTasks
where t == newTask
select t;
if (taskQuery.Count<DeveloperTask>() == 0)
{
Console.WriteLine($"Task {newTask.Name} was added to developer");
DeveloperTasks.Add(newTask);
}
}
finally
{
Lock.ExitWriteLock();
}
}
When a project team member needs to know about the status of a task, they call the IsTaskDone method, which uses a read lock on the ReaderWriterLockSlim by calling EnterReadLock and ExitReadLock:
public bool IsTaskDone(string taskName)
{
try
{
Lock.EnterReadLock();
var taskQuery = from t in DeveloperTasks
where t.Name == taskName
select t;
if (taskQuery.Count<DeveloperTask>() > 0)
{
DeveloperTask task = taskQuery.First<DeveloperTask>();
Console.WriteLine($"Task {task.Name} status was reported.");
return task.Status;
}
}
finally
{
Lock.ExitReadLock();
}
return false;
}
There are certain managerial members of the team who have the right to increase the priority of the tasks they assigned to the developer. They accomplish this by calling the IncreasePriority method on the Developer. IncreasePriority uses an upgradable lock on ReaderWriterLockSlim by first calling the EnterUpgradeable Lock method to acquire a read lock, and then, if the task is in the queue, upgrading to a write lock in order to adjust the priority of the task. Once the priority is adjusted,
the write lock is released, which degrades the lock back to a read lock, and that lock is released through a call to ExitUpgradeableReadLock:
public void IncreasePriority(string taskName)
{
try
{
Lock.EnterUpgradeableReadLock();
var taskQuery = from t in DeveloperTasks
where t.Name == taskName
select t;
if (taskQuery.Count<DeveloperTask>() > 0)
{
DeveloperTask task = taskQuery.First<DeveloperTask>();
Lock.EnterWriteLock(); task.Priority++;
Console.WriteLine($"Task {task.Name}" + $" priority was increased to {task.Priority}" + " for developer"); Lock.ExitWriteLock();
}
}
finally
{
Lock.ExitUpgradeableReadLock();
}
}
Discussion The ReaderWriterLockSlim was created to replace the existing ReaderWriterLock for a number of reasons:
ReaderWriterLock was more than five times slower than using a
Monitor.
Recursion semantics of ReaderWriterLock were not standard and were
broken in some thread reentrancy cases.
The upgrade lock method is nonatomic in ReaderWriterLock. While the
ReaderWriterLockSlim is only about two times slower than the Monitor,
it is more flexible and prioritizes writes, so in “few write, many read” scenarios, it is more scalable than the Monitor.
There are also methods to determine what type of lock is held as well
as how many threads are waiting to acquire it. By default, lock
acquisition recursion is disallowed. If you call EnterReadLock twice,
you get a LockRecursionException. You can enable lock recursion by passing a Lock RecusionPolicy.SupportsRecursion enumeration value to the constructor overload of ReaderWriterLockSlim that accepts it.
Even though it is possible to enable lock recursion, it is generally
discouraged, as it complicates matters and creates issues that are
not fun to debug.

Nested lock in Task.ContinueWith - Safe, or playing with fire?

Windows service: Generating a set of FileWatcher objects from a list of directories to watch in a config file, have the following requirements:
File processing can be time consuming - events must be handled on their own task threads
Keep handles to the event handler tasks to wait for completion in an OnStop() event.
Track the hashes of uploaded files; don't reprocess if not different
Persist the file hashes to allow OnStart() to process files uploaded while the service was down.
Never process a file more than once.
(Regarding #3, we do get events when there are no changes... most notably because of the duplicate-event issue with FileWatchers)
To do these things, I have two dictionaries - one for the files uploaded, and one for the tasks themselves. Both objects are static, and I need to lock them when adding/removing/updating files and tasks. Simplified code:
public sealed class TrackingFileSystemWatcher : FileSystemWatcher {
private static readonly object fileWatcherDictionaryLock = new object();
private static readonly object runningTaskDictionaryLock = new object();
private readonly Dictionary<int, Task> runningTaskDictionary = new Dictionary<int, Task>(15);
private readonly Dictionary<string, FileSystemWatcherProperties> fileWatcherDictionary = new Dictionary<string, FileSystemWatcherProperties>();
// Wired up elsewhere
private void OnChanged(object sender, FileSystemEventArgs eventArgs) {
this.ProcessModifiedDatafeed(eventArgs);
}
private void ProcessModifiedDatafeed(FileSystemEventArgs eventArgs) {
lock (TrackingFileSystemWatcher.fileWatcherDictionaryLock) {
// Read the file and generate hash here
// Properties if the file has been processed before
// ContainsNonNullKey is an extension method
if (this.fileWatcherDictionary.ContainsNonNullKey(eventArgs.FullPath)) {
try {
fileProperties = this.fileWatcherDictionary[eventArgs.FullPath];
}
catch (KeyNotFoundException keyNotFoundException) {}
catch (ArgumentNullException argumentNullException) {}
}
else {
// Create a new properties object
}
fileProperties.ChangeType = eventArgs.ChangeType;
fileProperties.FileContentsHash = md5Hash;
fileProperties.LastEventTimestamp = DateTime.Now;
Task task;
try {
task = new Task(() => new DatafeedUploadHandler().UploadDatafeed(this.legalOrg, datafeedFileData), TaskCreationOptions.LongRunning);
}
catch {
..
}
// Only lock long enough to add the task to the dictionary
lock (TrackingFileSystemWatcher.runningTaskDictionaryLock) {
try {
this.runningTaskDictionary.Add(task.Id, task);
}
catch {
..
}
}
try {
task.ContinueWith(t => {
try {
lock (TrackingFileSystemWatcher.runningTaskDictionaryLock) {
this.runningTaskDictionary.Remove(t.Id);
}
// Will this lock burn me?
lock (TrackingFileSystemWatcher.fileWatcherDictionaryLock) {
// Persist the file watcher properties to
// disk for recovery at OnStart()
}
}
catch {
..
}
});
task.Start();
}
catch {
..
}
}
}
}
What's the effect of requesting a lock on the FileSystemWatcher collection in the ContinueWith() delegate when the delegate is defined within a lock on the same object? I would expect it to be fine, that even if the task starts, completes, and enters the ContinueWith() before ProcessModifiedDatafeed() releases the lock, the task thread would simply be suspended until the creating thread has released the lock. But I want to make sure I'm not stepping on any delayed execution landmines.
Looking at the code, I may be able to release the lock sooner, avoiding the issue, but I'm not certain yet... need to review the full code to be sure.
UPDATE
To stem the rising "this code is terrible" comments, there are very good reasons why I catch the exceptions I do, and am catching so many of them. This is a Windows service with multi-threaded handlers, and it may not crash. Ever. Which it will do if any of those threads have an unhandled exception.
Also, those exceptions are written to future bulletproofing. The example I've given in comments below would be adding a factory for the handlers... as the code is written today, there will never be a null task, but if the factory is not implemented correctly, the code could throw an exception. Yes, that should be caught in testing. However, I have junior developers on my team... "May. Not. Crash." (also, it must shut down gracefully if there is an unhandled exception, allowing currently-running threads to complete - which we do with an unhandled exception handler set in main()). We have enterprise-level monitors configured to send alerts when application errors appear on the event log – those exceptions will log and flag us. The approach was a deliberate and discussed decision.
Each possible exception has each been carefully considered and chosen to fall into one of two categories - those that apply to a single datafeed and will not shut down the service (the majority), and those that indicate clear programming or other errors that fundamentally render the code useless for all datafeeds. For example, we've chosen to shut down the service down if we can't write to the event log, as that's our primary mechanism for indicating datafeeds are not getting processed. The exceptions are caught locally, because the local context is the only place where the decision to continue can be made. Furthermore, allowing exceptions to bubble up to higher levels (1) violates the concept of abstraction, and (2) makes no sense in a worker thread.
I'm surprised at the number of people who argue against handling exceptions. If I had a dime for every try..catch(Exception){do nothing} I see, you'd get your change in nickels for the rest of eternity. I would argue to the death1 that if a call into the .NET framework or your own code throws an exception, you need to consider the scenario that would cause that exception to occur and explicitly decide how it should be handled. My code catches UnauthorizedExceptions in IO operations, because when I considered how that could happen, I realized that adding a new datafeed directory requires permissions to be granted to the service account (it won't have them by default).
I appreciate the constructive input... just please don't criticize simplified example code with a broad "this sucks" brush. The code does not suck - it is bulletproof, and necessarily so.
1 I would only argue a really long time if Jon Skeet disagrees
First, your question: it's not a problem in itself to request lock inside ContinueWith. If you bother you do that inside another lock block - just don't. Your continuation will execute asynchronously, in different time, different thread.
Now, code itself is questionable. Why do you use many try-catch blocks around statements that almost cannot throw exceptions? For example here:
try {
task = new Task(() => new DatafeedUploadHandler().UploadDatafeed(this.legalOrg, datafeedFileData), TaskCreationOptions.LongRunning);
}
catch {}
You just create task - I cannot imagine when this can throw. Same story with ContinueWith. Here:
this.runningTaskDictionary.Add(task.Id, task);
you can just check if such key already exists. But even that is not necessary because task.Id is unique id for given task instance which you just created. This:
try {
fileProperties = this.fileWatcherDictionary[eventArgs.FullPath];
}
catch (KeyNotFoundException keyNotFoundException) {}
catch (ArgumentNullException argumentNullException) {}
is even worse. You should not use exceptions lile this - don't catch KeyNotFoundException but use appropriate methods on Dictionary (like TryGetValue).
So to start with, remove all try catch blocks and either use one for the whole method, or use them on statements that can really throw exceptions and you cannot handle that situation otherwise (and you know what to do with exception thrown).
Then, your approach to handle filesystem events is not quite scaleable and reliable. Many programs will generate multiple change events in short intervals when they are saving changes to a file (there are also other cases of multiple events for the same file going in sequence). If you just start processing file on every event, this might lead to different kind of troubles. So you might need to throttle events coming for a given file and only start processing after certain delay after last detected change. That might be a bit advanced stuff, though.
Don't forget to grab a read lock on the file as soon as possible, so that other processes cannot change file while you are working with it (for example, you might calculate md5 of a file, then someone changes file, then you start uploading - now your md5 is invalid). Other approach is to record last write time and when it comes to uploading - grab read lock and check if file was not changed in between.
What is more important is that there can be a lot of changes at once. Say I copied 1000 files very fast - you do not want to start uploading them all at once with 1000 threads. You need a queue of files to process, and take items from that queue with several threads. This way thousands of events might happen at once and your upload will still work reliably. Right now you create new thread for each change event, where you immediatly start upload (according to method names) - this will fail under serious load of events (and in cases described above).
No it will not burn you. Even if the ContinueWith is inlined into to the current thread that was running the new Task(() => new DatafeedUploadHandler().. it will get the lock e.g. no dead lock.
The lock statement is using the Monitor class internally, and it is reentrant. e.g. a thread can aquire a lock multiple times if it already got/owns the lock. Multithreading and Locking (Thread-Safe operations)
And the other case where the task.ContinueWith starts before the ProcessModifiedDatafeed finished is like you said. The thread that is running the ContinueWith simply would have to wait to get the lock.
I would really consider to do the task.ContinueWith and the task.Start() outside of the lock if you reviewed it. And it is possible based on your posted code.
You should also take a look at the ConcurrentDictionary in the System.Collections.Concurrent namespace. It would make the code easier and you dont have to manage the locking yourself. You are doing some kind of compare exchange/update here if (this.fileWatcherDictionary.ContainsNonNullKey(eventArgs.FullPath)). e.g. only add if not already in the dictionary. This is one atomic operation. There is no function to do this with a ConcurrentDictionary but there is an AddOrUpdate method. Maybe you can rewrite it by using this method. And based on your code you could safely use the ConcurrentDictionary at least for the runningTaskDictionary
Oh and TaskCreationOptions.LongRunning is literally creating a new thread for every task which is kind of an expensive operation. The windows internal thread pool is intelligent in new windows versions and is adapting dynamically. It will "see" that you are doing lots of IO stuff and will spawn new threads as needed and practical.
Greetings
I have not fully followed the logic of this code but are you aware that task continuations and calls to Wait/Result can be inlined onto the current thread? This can cause reentrancy.
This is very dangerous and has burned many.
Also I don't quite see why you are starting task delayed. This is a code smell. Also why are you wrapping the task creation with try? This can never throw.
This clearly is a partial answer. But the code looks very tangled to me. If it's this hard to audit it you probably should write it differently in the first place.

What is the best method for creating a static class that uses threads?

Let's say I am designing a simple logging class (yes - I know there are those already out there in the wild!) and I want the class to be static so the rest of my code can call it without having to instantiate it first. Maybe something like this:
internal static class Log
{
private static string _logFile = "";
internal static void InitializeLogFile(string path)
{
...
}
internal static void WriteHeader()
{
...
}
internal static void WriteLine(params string[] items)
{
...
}
}
Now, I want the internals to spin up their own thread and execute in an Asynch manner, possibly using BackgroundWorker to help simplify things. Should I just create a new BackgroundWorker in each method, create a static BackgroundWorker as a private property of the static class, or is there something I am overlooking altogether?
You definitely do not want spin up a new thread or BackgroundWorker on each invocation of the methods. I would use the producer-consumer pattern here. As it turns out this is such a common pattern that Microsoft provided us with the BlockingCollection class which simplies the implementation greatly. The nice thing about this approach is that:
there is only one extra thread required
the Log methods will have asynchronous semantics
the temporal ordering of the log messages is preserved
Here is some code to get your started.
internal static class Log
{
private static BlockingCollection<string> s_Queue = new BlockingCollection<string>();
static Log()
{
var thread = new Thread(Run);
thread.IsBackground = true;
thread.Start();
}
private static void Run()
{
while (true)
{
string line = s_Queue.Take();
// Add code to append the line to the log here.
}
}
internal static void WriteLine(params string[] items)
{
foreach (string item in items)
{
s_Queue.Add(item);
}
}
}
You only want to have 1 thread per log file/db. Otherwise, the order of items in the log is unreliable. Have a background thread that pulls from a thread-safe queue and does the writing.
Good call,
You definitely want the logging operations to occur in a separate thread as the code that is doing the logging. For instance, the accessor methods (such as "logEvent(myEvent)" ) should not block on file I/O operations while the logger logs the event to a file.
Make a queue so that the accessors simply push items onto the queue. This way your code shouldn't block while it is trying to log an event.
Start-up a second thread to empty the internal queue of events. This thread can run on a static private method of your logger class.
The performance drawback comes when you try to ensure thread safety of the underlying event queue. You will need to acquire a lock on the queue every time before a pop or push onto the queue.
Hope this helps.
I think that my recommendation is not exactly what you expect, but I hope it is useful anyway:
Don't use a static class. Instead,
use a regular class and hold a single
instance of it (the singleton
pattern); using a dependency
injection engine helps a lot with
this (I use MS Unity and it
works fine). If you define an interface for your logging class as well, your code will be much more testable.
As for the threading stuff, if I
understand correclty you want the
logging work to be performed in
separate threads. Are you sure that
you really need this? A logger should
be light enough so that you can
simple call the "Write" methods and
expect that your application
performance will not suffer.
A last note: you mention the BackgroundWorker class, but if I am not wrong this class is intended for use with desktop applications, not with ASP.NET. In this environment you should probably use something like the ThreadPool class.
Just my 2 euro cents...
I created a thread safe logging class myself a while back. I used it something like this.
Logging obj = new Logging(filename);
Action<string> log = obj.RequestLog();
RequestLog would return an anonymous method that wrote to its own Queue. Because a Q is thread safe for 1 reader/writer, I didn't need to use any locks when calling log()
The actual Logging object would create a new thread that ran in the background and would periodically check all of the queues. If a Q had a string in it, it would write it to a buffered file stream.
I added a little extra code to the reading thread so for each pass it made on the queues, if there was nothing written, it would sleep an extra 10 ms, up to a max of 100ms. This way the thread didn't spool too much. But if there was heavy writing going on, it would poll the Qs every 10ms.
Here's a snpit of the return code for the requested queue. The "this.blNewData = true" was so I didn't need to hit up every Q to see if any new data was written. No lock involved because a false positive still did no work since all the Qs would be empty anyway.
OutputQueue was the list of Queues that I looped through to see if anything was written. The code to loop through the list was in a lock in case NewQueueLog() was called and caused the list to get resized.
public Action<String> NewQueueLog()
{
Queue<String> tmpQueue = new Queue<String>(32);
lock (OutputQueue)
{
OutputQueue.Add(tmpQueue);
}
return (String Output) =>
{
tmpQueue.Enqueue(Output);
this.blNewData = true;
};
}
In the end, writing to the log was lock free, which helped when lots of threads were writing.

Multi-threaded file write enqueueing

So I have a static class that is supposed to be used as a log file manager, capable of adding "messages" (strings) to a Queue object, and that will push messages out to a file. Trouble is, many different threads should be enqueueing, and that the writer needs to be async as well. Currently when I insert into the queue, I'm also checking to see if the writer is writing (bool check), if it's not, i set the bool and then start the writing, but I'm getting intermittent IO exceptions about file access, and then wierd writing behavior sometimes.
Someone want to give me a hand on this?
If you don't want to restructure your code dramatically like I suggested in my other answer, you could try this, which assumes your LogManager class has:
a static thread-safe queue, _SynchronizedQueue
a static object to lock on when writing, _WriteLock
and these methods:
public static void Log(string message) {
LogManager._SynchronizedQueue.Enqueue(message);
ThreadPool.QueueUserWorkItem(LogManager.Write(null));
}
// QueueUserWorkItem accepts a WaitCallback that requires an object parameter
private static void Write(object data) {
// This ensures only one thread can write at a time, but it's dangerous
lock(LogManager._WriteLock) {
string message = (string)LogManager._SynchronizedQueue.Dequeue();
if (message != null) {
// Your file writing logic here
}
}
}
There's only one problem: the lock statement in the Write method above will guarantee only one thread can write at a time, but this is dangerous. A lot can go wrong when trying to write to a file, and you don't want to hold onto (block) thread pool threads indefinitely. Therefore, you need to use a synchronization object that lets you specify a timeout, such as a Monitor, and rewrite your Write method like this:
private static void Write() {
if (!Monitor.TryEnter(LogManager._WriteLock, 2000)) {
// Do whatever you want when you can't get a lock in time
} else {
try {
string message = (string)LogManager._SynchronizedQueue.Dequeue();
if (message != null) {
// Your file writing logic here
}
}
finally {
Monitor.Exit(LogManager._WriteLock);
}
}
}
It sounds like the queue is driving the file writing operation. I recommend that you invert the control relationship so that the writer drives the process and checks the queue for work instead.
The simplest way to implement this is to add a polling mechanism to the writer in which it checks the queue for work at regular intervals.
Alternately, you could create an observerable queue class that notifies subscribers (the writer) whenever the queue transitions from empty: the subscribing writer could then begin its work. (At this time, the writer should also unsubscribe from the queue's broadcast, or otherwise change the way it reacts to the queue's alerts.)
After completing its job, the writer then checks the queue for more work. If there's no more work to do, it goes to sleep and resume polling or goes to sleep and resubscribes to the queue's alerts.
As Irwin noted in his answer, you also need to use the thread-safe wrapper provided by the Queue class' Synchronized method or manually synchronize access to your Queue if multiple threads are reading from it and writing to it (as in SpaceghostAli's example).
I would have just one thread doing the writes to avoid contentions, while i would use multiple threads to enqueue.
You are advised "To guarantee the thread safety of the Queue, all operations must be done through the wrapper returned by the Synchronized method." - from http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx
You should synchronize around your queue. Have multiple threads send to the queue and a single thread read from the queue and write to the file.
public void Log(string entry)
{
_logMutex.WaitOne();
_logQueue.Enqueue(entry);
_logMutex.ReleaseMutex();
}
private void Write()
{
...
_logMutex.WaitOne();
string entry = _logQueue.Dequeue();
_logMutex.ReleaseMutex();
_filestream.WriteLine(entry);
...
}
Let me address the problem at a different level:
If your writing a business application then you'd want to focus on the business-logic portions rather than the infrastructural code, more so if this infra code is already available, tested and deployed to multiple production sites (taking care of your NFRs)
I'm sure you're aware of the existance of logging frameworks like log4net and others http://csharp-source.net/open-source/logging.
Have you given these a chance before hand-rolling out your own Logger?
Take this option to the technical architect of the enterprise you're writing for and see she thinks.
Cheers

Should Locks and Mutexes in C# be used together

Wouldn't this be overkill and only one of these necessary? I've searched and found different posts about Mutual Exclusion and locks in C# here and here.
Example:
In our app, we have a function that spins off multiple reconnection threads and inside this thread we use a Mutex and a lock. Wouldn't lock block access to this section of code and prevent connect from being updated by any other thread?
bool connect = false;
Mutex reconnectMutex = new Mutex(false, "Reconnect_" + key);
try
{
lock(site)
{
if(site.ContainsKey(key))
{
siteInfo = (SiteInfo)site[key];
if(reconnectMutex.WaitOne(100, true))
{
connect = true;
}
}
}
if (connect)
{
// Process thread logic
}
}
catch
{}
reconnectMutex.ReleaseMutex();
More Info:
This is in an ASP.NET WebService not running in a Web Garden.
That Mutex (because it has a name) will stop any process on the same machine accessing it as well, whereas lock will only stop other threads in the same process. I can't see from that code sample why you'd need both kinds of lock. It seems good practice to hold the simple lock for a short period of time - but then the much heavier interprocess mutex is locked for a probably longer (though overlapping) period! Would be simpler to just use the mutex. And perhaps to find out whether an interprocess lock is really necessary.
By the way, catch {} is absolutely the wrong thing to use in that scenario. You should use finally { /* release mutex */ }. They are very different. The catch will swallow far more kinds of exception than it should, and will also cause nested finally handlers to execute in response to low-level exceptions such as memory corruption, access violation, etc. So instead of:
try
{
// something
}
catch
{}
// cleanup
You should have:
try
{
// something
}
finally
{
// cleanup
}
And if there are specific exceptions you can recover from, you could catch them:
try
{
// something
}
catch (DatabaseConfigurationError x)
{
// tell the user to configure the database properly
}
finally
{
// cleanup
}
"lock" is basically just a syntactic sugar for Montor.Enter/Exit. Mutex is a multi-process lock.
They have very different behavior. There is nothing wrong with using both in the same application or methods, since they're designed to block different things.
However, in your case, I think you may be better off looking into Semaphore and Monitor. It doesn't sound like you need to lock across processes, so they are probably a better choice in this situation.
As others have pointed out, the Mutex locks across processes and the local lock (Monitor) locks only those threads owned by the current process. However ...
The code you showed has a pretty serious bug. It looks like you're releasing the Mutex unconditionally at the end (i.e. reconnectMutex.ReleaseMutex()), but the Mutex is only acquired if site.ContainsKey() returns true.
So if site.ContainsKey returns false, then releasing the Mutex is going to throw ApplicationException because the calling thread does not own the Mutex.
You didn't give enough info to really answer this. As already stated by Earwicker a Mutex allows you to have a synchronization accross processes. Thus if you have two instances of the same app running you can serialize access. You might do this for example when using external resources.
Now you lock on site protects site from access by other threads in the same process. This might be nessecary depending on what other methods / threads are doing. Now if this is the only place that site is being locked then yes I would think it is overkill.

Categories