views:

704

answers:

7

I'd like to know how to implement the following restriction: One method in my Windows Service should not be called again before the earlier call has been finished. The method in question goes thru couple of database tables and it's very important that this process won't be called again before it's finished. I have a setting that defines how often my service will activate and under normal circumstances it never activates before the earlier call has been finished (because whole process should not take more than couple of minutes and the interval is set to 10 minutes) but thats not sure enough. I guess.

How to implement this?

+11  A: 

You can use a named Mutex or a named Semaphore to ensure that only one holder of the Mutex/Semaphore is executing at once. As a commenter pointed out, keep in mind you must be careful not to abandon a mutex or improperly acquire/release a semaphore.

sixlettervariables
I like your answer better
Quintin Robinson
I'd rather use a named semaphore, no risk of getting an abandoned mutex then so no need to have to deal with the additional possibility of experiencing the exception or having to think about ownership.
Peter Morris
+1 Peter Morris, I'll add a blurb about the possibility of a named Semaphore, however, they too have problems with improper releasing, etc
sixlettervariables
+1  A: 

Well if all the code is localized you can set a boolean and check the boolean before executing the method, otherwise you can IPC and request the state before execution.

Quintin Robinson
+3  A: 

One way would be to use locking:

private readonly object myLock = new object();

private void MyMethod()
{
   lock(myLock)
   {
      //code goes here
   }
}

This ensures that this method can never be running more that once at a time.

Jon Tackabury
don't forget to make myLock static - the question actually implies multythreaded situation.
DK
I believe it is actually a multi-process situation.
sixlettervariables
+1  A: 

Some alternatives:

  1. You can put a check in the call to check some flag or call Monitor.TryEnter and return with an error/do nothing if negative.
  2. You can queue up calls (if you need this method to execute more than once) and only invoke when Monitor has been signaled.
  3. If you don't mind blocking, and the method is on a separate thread, you can join the thread of the method you want to wait.

I'm sure there are others.

DavidN
A: 

This sounds like a serial workflow... Have you considered using a workflow framework?

Chuck Conway
+1  A: 

I second the Mutex suggestion, but you might also want to take a look at transactions. Wrap your entire code in a transaction (this requires a using System.Transactions):

using(TransactionScope scope = new TransactionScope())
{
    try 
    {
        /* ... your current code here */
        scope.Complete();
    }
    catch (Exception e)
    {
        /* Any appropriate error handling/logging here */
    }
    finally
    {
    }
}

A transactionscope automatically locks all related tables. You can reduce the restrictions and allow other processes to read, but not write to the data that your process is touching. You do this by passing options to the TransactionsScope constructor.

Jeroen Huinink
Odd, all of that message didn't get sent to me the first time, bug in the system?
Rob
Glitch in my typing. I accidentaly submitted my reply before finishing the complete post.
Jeroen Huinink
A transaction alone cannot be used to synchronize code. It can only be used to *consistently* access data. How tables are locked, or if at all, depends on the isolation-level of the transaction. That is totally different than using a mutex (things like SQL*Server's sp_get_applock() set aside).
Christian.K
A: 

If you don't mind restricting one thread at a time to the entire object, then you can use:

Synchronization Contexts

  1. Have your class inherit from ContextBoundObject
  2. Apply a [Synchronization] attribute to the class.

The CLR will only allow one thread at a time to execute code per instance of this class. The others will block until the lock is released by the current thread.

mbeckish