Question

I'm trying to find a way to provide exclusive access to a resource, while at the same time providing information about the state of the lock (_isWorking) for other classes to read.

Here's what I have come up with so far :

    private int _isWorking = 0;

    public bool IsWorking
    {
        get { return Interlocked.CompareExchange(ref _isWorking, 0, 0) == 1; }
    }

    public void LaunchProcess()
    {
        if (Interlocked.CompareExchange(ref _isWorking, 1, 0) == 0)
        {
            try
            {
                DoExpansiveProcess();
            }
            finally
            {
                Interlocked.Exchange(ref _isWorking, 0);
            }
        }
    }

    public void DoExpansiveProcess()
    {
        Thread.Sleep(30000);
    }

It does not work exactly as a lock since the second thread, upon seeing that a thread is already running the process, will just leave the method and do nothing.

Is it the correct way to do this or are there any C# 3.5 structure more adapted for the purpose?

And how to implement a 'real' lock scenario, where the second thread would still execute the method after the first one finishes, while providing information about the state?

Was it helpful?

Solution

Assuming that I understand your question correctly then I think what you are after is the Monitor.TryEnter method with a timeout value of 0. It will attempt to acquire the lock, but with a timeout of 0 it will always return immediately with value indicating whether the lock was actually acquired. The implication here is that if the lock was not acquired then it is assumed that it is already acquired by someone else. The problem is that if returns true then you would have to immediately release it with a call to Monitor.Exit.

Perhaps this would be a better solution:

public class Worker
{
  private Object m_LockObject = new Object();
  private volatile bool m_IsWorking = false;

  public bool IsWorking
  {
    get { return m_IsWorking; }
  }

  public void LaunchProcess()
  {
    lock (m_LockObject)
    {
      m_IsWorking = true;
      DoExpansiveProcess();
      m_IsWorking = false;
    }
  }
}

OTHER TIPS

System.Threading.ManualResetEvent might be able to help here. Just do a WaitOne() against it. If it's being used then it will come back as false. If it isn't in use it is available.

One of the WaitOne() overloads takes a time in milliseconds to wait. If you use WaitOne(0) then the wait will return immediately in a non blocking way.

You should do some research into the synchronisation primitives that .Net offers:

System.Threading.Monitor lock() { }
System.Threading.ManualResetEvent
System.Threading.AutoResetEvent
System.Threading.Semaphore
System.Threading.Mutex

I think your approach should in general be correct and will definitely be much faster than using a ManualResetEvent (if its at all relevant) - because ManualResetEvent wraps underlying unmanaged primitive.

To make this approach safe however, _isWorking has to be private.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top