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.
Related
The true power of semaphore is :
Limits the number of threads that can access a resource or pool of
resources concurrently
That is understood and clear.
But I never got a chance to play with the overload of Wait which accepts a timeout integer, however - this seems to allow multiple threads get into the critical section although I've explicitly set semaphore not to allow more than one thread at a time:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);
private void Main()
{
Task.Run(() => DelayAndIncrementAsync());
Task.Run(() => DelayAndIncrementAsync());
}
private void DelayAndIncrementAsync()
{
_mutex.Wait(2000);
try
{
Console.WriteLine(0);
Thread.Sleep(TimeSpan.FromSeconds(5));
Console.WriteLine(1);
}
finally
{
_mutex.Release();
}
}
The first thread is entering the mutex zone, prints "0", waits 5 seconds, meanwhile after 2 seconds the other thread ALSO enters the critical section?
Question
Isn't it defeating the whole purpose of semaphore?
What are the real life scenarios which I would use this timeout, especially when the basic rule is -
"Semaphore = Limits the number of threads that can access a resource
or pool of resources concurrently
You need to check the return value of the wait. The Timeout based wait will try for 2 seconds to take the mutex then return. You need to check if the return value is true (i.e you have the mutex) or not.
Edit: Also keep in mind that the timeout based wait will return immediately if the semaphore is available, so you cant use this to prevent an infinite loop in the code via this technique.
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);
void Main()
{
Task.Run(()=>DelayAndIncrementAsync());
Task.Run(()=>DelayAndIncrementAsync());
}
public void DelayAndIncrementAsync()
{
if (_mutex.Wait(2000))
{
try
{
Console.WriteLine(0);
Thread.Sleep(TimeSpan.FromSeconds(5));
Console.WriteLine(1);
}
finally
{
_mutex.Release();
}
} else {
//oh noes I don't have the mutex
}
}
Your misconception is that there is an implicit "mutex zone" which is not defined by you.
The overload of Wait which you are using returns a boolean value which tells you whether or not the mutex was successfully entered.
What you are doing in your example is entering the critical zone whether or not the thread has acquired the mutex, making it redundant.
Generally, you would want to use this overload in any situation where you want to try to enter a mutex but also have a fallback strategy in case that it is not currently possible to acquire the mutex within the allotted time.
This will make people cringe but using the timeout (and confirming it did timeout) is a good way to log and track deadlock bugs. Sure if you wrote your program correctly you wouldn't need these, but I've personally used this for this purpose which has saved me a lot of time.
So yes it does defeat the purpose (in most cases) if you let it timeout and then hit the critical section with multiple threads. But it can be useful to log or detect a deadlock bug.
There are also use cases where you want multiple threads to access the critical section, but only in specific scenarios. Eg it would not be fatal and simply be undesirable for it occur. Eg you aren't using the semaphore to stop a cross thread crash, but rather something else.
It seems that Monitor doesn't work as expected in WinRT store applications.
I have the following code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var tasks = Enumerable.Range(0, 10).Select((i)=>new Task(DoWork)).ToArray();
foreach (var task in tasks)
{
task.Start();
}
Task.WaitAll(tasks);
}
static object lockObject = new Object();//typeof(MainPage)
protected async void DoWork()
{
bool taken =false;
Monitor.Enter(lockObject, ref taken);
Debug.WriteLine("In");
await Task.Delay(1000);
Debug.WriteLine("Out");
if (taken) Monitor.Exit(lockObject);
}
In the output window I see:
In
In
In
In
In
In
In
Out
Out
Out
Out
Out
Out
Out
In
Out
A first chance exception of type 'System.Threading.SynchronizationLockException' occurred in App4.exe
Which mean that Monitor is not locking the critical area.
Does anybody has a clue what I'm doing wrong?
You're effectively trying to use:
lock (lockObject)
{
await Task.Delay(1000);
}
... except that the C# compiler wouldn't allow you to do that, because it would be broken. By the time your await expression completes, you can be on a different thread - so when you call Monitor.Exit, you may well not be on the same thread as you acquired the lock in... hence the exception.
I suggest that you change your logging to show:
When you call In, what thread you're on and the value of taken afterwards (you'll probably see that some tasks haven't successfully taken the monitor, because another thread owns it - but see below)
Before you call Monitor.Exit, what thread you're on
It's not clear what you're trying to achieve, but using Monitor here is almost certainly the wrong approach.
Also note that because multiple tasks can all execute on the same thread (not at the same time, but you're "giving up" the thread with await) and because monitors are reentrant (one thread can acquire a monitor multiple times) you may well see multiple tasks acquiring the monitor.
It's important that you understand why this doesn't work - and that you understand that a thread isn't the same as a task. Then you can try to start working out how to actually achieve what you want, which almost certainly isn't via Monitor.
Monitor doesn't work with async methods.
If you want async-compatible mutual exclusion, try SemaphoreSlim.WaitAsync or one of the coordination primitives in my AsyncEx library.
I post a lot here regarding multithreading, and the great stackoverflow community have helped me alot in understand multithreading.
All the examples I have seen online only deal with one thread.
My application is a scraper for an insurance company (family company ... all free of charge). Anyway, the user is able to select how many threads they want to run. So lets say for example the user wants the application to scrape 5 sites at one time, and then later in the day he choses 20 threads because his computer isn't doing anything else so it has the resources to spare.
Basically the application builds a list of say 1000 sites to scrape. A thread goes off and does that and updates the UI and builds the list.
When thats finished another thread is called to start the scraping. Depending on the number of threads the user has set to use it will create x number of threads.
Whats the best way to create these threads? Should I create 1000 threads in a list. And loop through them? If the user has set 5 threads to run, it will loop through 5 at a time.
I understand threading, but it's the application logic which is catching me out.
Any ideas or resources on the web that can help me out?
You could consider using a thread pool for that:
using System;
using System.Threading;
public class Example
{
public static void Main()
{
ThreadPool.SetMaxThreads(100, 10);
// Queue the task.
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
Console.WriteLine("Main thread does some work, then sleeps.");
Thread.Sleep(1000);
Console.WriteLine("Main thread exits.");
}
// This thread procedure performs the task.
static void ThreadProc(Object stateInfo)
{
Console.WriteLine("Hello from the thread pool.");
}
}
This scraper, does it use a lot of CPU when its running?
If it does a lot of communication with these 1000 remote sites, downloading their pages, that may be taking more time than the actual analysis of the pages.
And how many CPU cores does your user have? If they have 2 (which is common these days) then beyond two simultaneous threads performing analysis, they aren't going to see any speed up.
So you probably need to "parallelize" the downloading of the pages. I doubt you need to do the same for the analysis of the pages.
Take a look into asynchronous IO, instead of explicit multi-threading. It lets you launch a bunch of downloads in parallel and then get called back when each one completes.
If you really just want the application, use something someone else already spent time developing and perfecting:
http://arachnode.net/
arachnode.net is a complete and comprehensive .NET web crawler for
downloading, indexing and storing
Internet content including e-mail
addresses, files, hyperlinks, images,
and Web pages.
Whether interested or involved in
screen scraping, data mining, text
mining, research or any other
application where a high-performance
crawling application is key to the
success of your endeavors,
arachnode.net provides the solution
you need for success.
If you also want to write one yourself because it's a fun thing to write (I wrote one not long ago, and yes, it is alot of fun ) then you can refer to this pdf provided by arachnode.net which really explains in detail the theory behind a good web crawler:
http://arachnode.net/media/Default.aspx?Sort=Downloads&PageIndex=1
Download the pdf entitled: "Crawling the Web" (second link from top). Scroll to Section 2.6 entitled: "2.6 Multi-threaded Crawlers". That's what I used to build my crawler, and I must say, I think it works quite well.
I think this example is basically what you need.
public class WebScraper
{
private readonly int totalThreads;
private readonly List<System.Threading.Thread> threads;
private readonly List<Exception> exceptions;
private readonly object locker = new object();
private volatile bool stop;
public WebScraper(int totalThreads)
{
this.totalThreads = totalThreads;
threads = new List<System.Threading.Thread>(totalThreads);
exceptions = new List<Exception>();
for (int i = 0; i < totalThreads; i++)
{
var thread = new System.Threading.Thread(Execute);
thread.IsBackground = true;
threads.Add(thread);
}
}
public void Start()
{
foreach (var thread in threads)
{
thread.Start();
}
}
public void Stop()
{
stop = true;
foreach (var thread in threads)
{
if (thread.IsAlive)
{
thread.Join();
}
}
}
private void Execute()
{
try
{
while (!stop)
{
// Scrap away!
}
}
catch (Exception ex)
{
lock (locker)
{
// You could have a thread checking this collection and
// reporting it as you see fit.
exceptions.Add(ex);
}
}
}
}
The basic logic is:
You have a single queue in which you put the URLs to scrape then you create your threads and use a queue object to which every thread has access. Let the threads start a loop:
lock the queue
check if there are items in the queue, if not, unlock queue and end thread
dequeue first item in the queue
unlock queue
process item
invoke an event that updates the UI (Remember to lock the UI Controller)
return to step 1
Just let the Threads do the "get stuff from the queue" part (pulling the jobs) instead of giving them the urls (pushing the jobs), that way you just say
YourThreadManager.StartThreads(numberOfThreadsTheUserWants);
and everything else happens automagically. See the other replies to find out how to create and manage the threads .
I solved a similar problem by creating a worker class that uses a callback to signal the main app that a worker is done. Then I create a queue of 1000 threads and then call a method that launches threads until the running thread limit is reached, keeping track of the active threads with a dictionary keyed by the thread's ManagedThreadId. As each thread completes, the callback removes its thread from the dictionary and calls the thread launcher.
If a connection is dropped or times out, the callback reinserts the thread back into the queue. Lock around the queue and the dictionary. I create threads vs using the thread pool because the overhead of creating a thread is insignificant compared to the connection time, and it allows me to have a lot more threads in flight. The callback also provides a convenient place with which to update the user interface, even allowing you to change the thread limit while it's running. I've had over 50 open connections at one time. Remember to increase your MacConnections property in your app.config (default is two).
I would use a queue and a condition variable and mutex, and start just the requested number of threads, for example, 5 or 20 (and not start 1,000).
Each thread blocks on the condition variable. When woken up, it dequeues the first item, unlocks the queue, works with the item, locks the queue and checks for more items. If the queue is empty, sleep on the condition variable. If not, unlock, work, repeat.
While the mutex is locked, it can also check if the user has requested the count of threads to be reduced. Just check if count > max_count, and if so, the thread terminates itself.
Any time you have more sites to queue, just lock the mutex and add them to the queue, then broadcast on the condition variable. Any threads that are not already working will wake up and take new work.
Any time the user increases the requested thread count, just start them up and they will lock the queue, check for work, and either sleep on the condition variable or get going.
Each thread will be continually pulling more work from the queue, or sleeping. You don't need more than 5 or 20.
Consider using the event-based asynchronous pattern (AsyncOperation and AsyncOperationManager Classes)
You might want to take a look at the ProcessQueue article on CodeProject.
Essentially, you'll want to create (and start) the number of threads that are appropriate, in your case that number comes from the user. Each of these threads should process a site, then find the next site needed to process. Even if you don't use the object itself (though it sounds like it would suit your purposes pretty well, though I'm obviously biased!) it should give you some good insight into how this sort of thing would be done.
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
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.