call_once for C#
Join the DZone community and get the full member experience.
Join For FreeOne 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 asthe
call_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…
Published at DZone with permission of Dror Helper, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments