Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

call_once for C#

DZone's Guide to

call_once for C#

·
Free Resource

One of the useful gems that made it into C++11 Standard Template Libraries (STD) is call_once, this nifty little method makes sure that specific code is called only once (duh) and it follows these 3 rules:

  • Exactly one execution of exactly one of the functions (passed as f to the invocations in the group) is performed. It is undefined which function will be selected for execution. The selected function runs in the same thread as

    thecall_once invocation it was passed to.

  • No invocation in the group returns before the abovementioned execution of the selected function is completed successfully, that is, doesn't exit via an exception.
  • If the selected function exits via exception, it is propagated to the caller. Another function is then selected and executed.

I needed something similar – I had a method that should only be called once (initialize) and I wanted to implement something similar to the call_once I’ve been using for my C++ development.

My first object was to try and make it as preferment as possible and so I’ve looked for a solution that does not involve locks:

public static class Call
{
    public static void Once(OnceFlag flag,  Action action)
    {
        if (flag.CheckIfNotCalledAndSet)
        {
            action.Invoke();            
        }
    }
}

since I was  trying to mimic the C++ code I wrote two objects Call (above) and OnceFlag which has all of the magic inside using Interlocked:

public class OnceFlag
{
    private const int NotCalled = 0;
    private const int Called = 1;
    private int _state = NotCalled;
    internal bool CheckIfCalledAndSet
    {
        get
        {
            var prev = Interlocked.Exchange(ref _state, Called);
            
            return prev == NotCalled;
        }
    }

    internal void Reset()
    {
        Interlocked.Exchange(ref _state, NotCalled);
    }
}

I’m using Interlocked as a thread-safe way to check & set the value making sure that only once it would return true – try it:

class Program
{
    static OnceFlag _flag = new OnceFlag();
    static void Main(string[] args)
    {
        var t1 = new Thread(() => DoOnce(1));
        var t2 = new Thread(() => DoOnce(2));
        var t3 = new Thread(() => DoOnce(3));
        var t4 = new Thread(() => DoOnce(4));

        t1.Start();
        t2.Start();
        t3.Start();
        t4.Start();

        t1.Join();
        t2.Join();
        t3.Join();
        t4.Join();
    }

    private static void DoOnce(int index)
    {
        Call.Once(_flag, () => Console.WriteLine("Callled (" + index + ")"));
    }
}

It’s very simple solution unfortunately not entirely correct –  the method used will only be called once, but requirements 2 & 3 were not implemented. Luckily for me I didn’t need to make sure that exception enable another call to pass through nor did I need to block other calls until the first call finishes.

But I wanted to try and write a proper implementation, unfortunately not as preferment due to the use of locks:

public static void Once(OnceFlagSimple flag, Action action)
{
    lock (flag)
    {        
        if (flag.CheckIfNotCalled)
        {
            action.Invoke();
    
            flag.Set();            
        }
    }
}

But it works, and since I’m already using lock I can split the check and Set methods and use a bool value inside the flag instead of Interlocked.

  • All other threads are blocked due to lock until first finish running – check!
  • In case of exception other method can execute the once block – check!
  • If exited properly the block would only execute once – check!

But not very good performance due to locking even after the first time run.

I’m still looking for a better way to implement call_once – it’s a good exercise in threading and I might find a cool new ways to use the classes under Threading or Task namespaces.

please let me know if you have a better implementation – that’s what the comments are for…

Topics:

Published at DZone with permission of Dror Helper, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}