Click here to Skip to main content
Click here to Skip to main content
Go to top

Locking for internal operations

, 1 Apr 2013
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Mehdi Gholam
Architect
United Kingdom United Kingdom
Mehdi first started programming when he was 8 on BBC+128k machine in 6512 processor language, after various hardware and software changes he eventually came across .net and c# which he has been using since v1.0.
He is formally educated as a system analyst Industrial engineer, but his programming passion continues.
 
* Mehdi is the 5th person to get 6 out of 7 Platinums on CodeProject (13th Jan'12)

Comments and Discussions

 
QuestionShould internalOperation be volatile? PinmemberFatCatProgrammer2-Apr-13 5:20 
AnswerRe: Should internalOperation be volatile? PinmemberAndrew Rissing2-Apr-13 5:52 
GeneralMy vote of 1 PinmemberAndrew Rissing1-Apr-13 6:22 
GeneralRe: My vote of 1 PinmvpMehdi Gholam1-Apr-13 9:19 
GeneralRe: My vote of 1 PinmemberAndrew Rissing2-Apr-13 5:47 
GeneralRe: My vote of 1 PinmvpMehdi Gholam2-Apr-13 7:28 
GeneralRe: My vote of 1 PinmemberAndrew Rissing2-Apr-13 8:01 
BugUnintended side effects... PinmemberAndrew Rissing29-Mar-13 6:40 
GeneralRe: Unintended side effects... PinmvpMehdi Gholam29-Mar-13 20:09 
GeneralRe: Unintended side effects... PinmvpMehdi Gholam30-Mar-13 21:16 
BugRe: Unintended side effects... PinmemberAndrew Rissing1-Apr-13 6:21 
SuggestionReaderWriterLockSlim PinmemberAndrew Rissing29-Mar-13 6:22 
GeneralRe: ReaderWriterLockSlim PinmvpMehdi Gholam29-Mar-13 20:12 
I found this : http://www.heikniemi.net/hardcoded/2009/12/readerwriterlockslim-performance/[^]
Its the man, not the machine - Chuck Yeager
If at first you don't succeed... get a better publicist
If the final destination is death, then we should enjoy every second of the journey.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 1 Apr 2013
Article Copyright 2013 by Mehdi Gholam
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid