Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Locking for internal operations

0.00/5 (No votes)
1 Apr 2013 1  
Pausing external operations on an object when you want to lock the object for internal operations.

Introduction 

There are times when you want to stop the external operations on an object to do internal operations, say for example you need to cleanup internal data structures to free up memory so you need to halt the use of the object in the meantime.

Although we are using locks to do this we are not blocking the external operations of the object so the object will retain multi-thread support and the operations are concurrent.

Using the code 

Below is the class using this technique. We have a class with three external methods: dash, dot, and freeup, when freeup is called the other two methods will block until done so you are sure that freeup can do all that it needs without worrying about concurrency issues. 

All you need to do for your own classes is to add the CheckLock method at the start of your own methods, so there is minimal changes to the existing code.

class someclass
{
    private bool _internalOperation; // flag for checking if we need to lock
    private object _lock = new object();

    public void dash()
    {
        CheckLock(); // blocks until internal operation is done
        Console.Write("-");
    }

    public void dot()
    {
        CheckLock(); // just add this line to your own code
        Console.Write(".");
    }

    public void freeup()
    {        
        lock (_lock)
        {
            _internalOperation = true;
            // some really important operation here
            for (int i = 0; i < 100; i++)
            {
                Console.Write("F");
                Thread.Sleep(20);
            }
            _internalOperation = false;
        }        
    }

    private void CheckLock()
    {
        if (_internalOperation) // don't lock unless needed
            lock (_lock) ; // the good stuff is here
    }
} 

As you can see we are using locks in the freeup method but not in the other methods so we retain concurrency of operations on the object i.e., dash and dot do not block each other.

To illustrate this below is the sample code that uses the class above:

class Program
{
    public delegate void MethodCall();
    public static bool end = false;

    static void Main(string[] args)
    {
        someclass s = new someclass();

        Task.Factory.StartNew(() =>
        {
            while (!end)
            {
                Thread.Sleep(2000);
                s.freeup();
            }
        });

        Task.Factory.StartNew( () => Exec(s.dot) );

        Task.Factory.StartNew( () => Exec(s.dash) );

        Task.Factory.StartNew(() =>
        {
            Console.ReadKey();
            end = true;
        });

        while (!end)
            Thread.Sleep(1000);
    }

    private static void Exec(MethodCall method)
    {
        while (!end)
        {
            method();
            Thread.Sleep(5);
        }
    }
} 

The above code uses .NET 4 Task framework and starts four threads: one to call the dash, one for the dot, one to exit the program if you press any key, and one to call the freeup every two seconds. So you will see a series of '-' and '.' in the console output with a series of 'F' characters which show that the object is multi-threaded and blocks for the internal operation so you don't see anything break up the 'F' characters.

v2 -  mindful of race conditions

To get around race conditions and to make the use of the code easier I have changed the code to use a helper class and a queue as below:

    class someclass
    {
        // add this class to your own code
        class L : IDisposable
        {
            someclass _sc;
            public L(someclass sc) // change to the class name
            {
                _sc = sc;
                _sc.CheckLock();
            }
            void IDisposable.Dispose()
            {
                _sc.Done();
            }
        }
        private volatile bool _internalOperation;
        private Queue _que = new Queue(); // added a queue
        private object _lock = new object();

        public void dash()
        {
            using (new L(this)) // better and cleaner way to wrap things
            {
                // your normal code goes here
                Console.Write("-");
            }
        }

        public void dot()
        {
            using (new L(this))
            {
                Console.Write(".");
            }
        }

        public void freeup()
        {
            lock (_lock)
            {
                _internalOperation = true;
                while (_que.Count > 0) Thread.SpinWait(1); // wait till pending operations are done

                for (int i = 0; i < 100; i++)
                {
                    Console.Write("F");
                    Thread.Sleep(20);
                }
                _internalOperation = false;
            }
        }

        private void CheckLock()
        {
            if (_internalOperation)
                lock (_lock) ;
            _que.Enqueue(1);
        }

        private void Done()
        {
            if (_que.Count > 0)
                _que.Dequeue();
        }
    }

A couple of things to do in your own code and you are good to go:

  • Add the L class to your own code.
  • Change the class type passed to the L class (i.e. someclass to your own class).
  • Add a Queue to your main class.
  • Wrap your public methods with the using(new L(this)) line.
The queue ensures that all pending operations are completed before the freeup() starts and all public operations stop in the meantime.

Points of Interest

Personally I used to use Thread.Sleep a lot for these types of checking which took a performance toll and looked really ugly, and while thinking about it I came to this solution which seems much cleaner.

The if statement in CheckLock insures that you do not take a performance hit in locking when you don't need to.

A very important point is that the public methods on the main class are concurrent and do not lock each other out so you can call them concurrently.

History 

  • Initial release: 28th March 2013.
  • Update :  31st March 2013
    • Moved changing _internalOperation into the lock in freeup()
  • Update : 1st April 2013
    • New symantics using a queue and a helper class

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here