I have a class which uses a Timer. This class implements IDispose. I would like to wait in the Dispose method until the timer will not fire again.
I implement it like this:
private void TimerElapsed(object state)
{
// do not execute the callback if one callback is still executing
if (Interlocked.Exchange(ref _timerIsExecuting, 1) == 1)
return;
try
{
_callback();
}
finally
{
Interlocked.Exchange(ref _timerIsExecuting, 0);
}
}
public void Dispose()
{
if (Interlocked.Exchange(ref _isDisposing, 1) == 1)
return;
_timer.Dispose();
// wait until the callback is not executing anymore, if it was
while (_timerIsExecuting == 0)
{ }
_callback = null;
}
Is this implementation correct? I think it mainly depends on the question if _timerIsExecuting == 0 is an atomic operation. Or would I have to use a WaitHandle. For me it seems it would make the code unnecessarily complicated...
I am not an expert in multi-threading, so would be happy about any advice.
Unless you have a reason not to use System.Threading.Timer
This has a Dispose method with a wait handle
And you can do something like,
private readonly Timer Timer;
private readonly ManualResetEvent TimerDisposed;
public Constructor()
{
Timer = ....;
TimerDisposed = new ManualResetEvent(false);
}
public void Dispose()
{
Timer.Dispose(TimerDisposed);
TimerDisposed.WaitOne();
TimerDisposed.Dispose();
}
Generally one can use the Timer.Dispose(WaitHandle) method, but there's a few pitfalls:
Pitfalls
Support for multiple-disposal (see here)
If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times. Instance methods other than Dispose can throw an ObjectDisposedException when resources are already disposed.
Timer.Dispose(WaitHandle) can return false. It does so in case it's already been disposed (i had to look at the source code). In that case it won't set the WaitHandle - so don't wait on it! (Note: multiple disposal should be supported)
not handling a WaitHandle timeout. Seriously - what are you waiting for in case you're not interested in a timeout?
Concurrency issue as mentioned here on msdn where an ObjectDisposedException can occur during (not after) disposal.
Timer.Dispose(WaitHandle) does not work properly with -Slim waithandles, or not as one would expect. For example, the following does not work (it blocks forever):
using(var manualResetEventSlim = new ManualResetEventSlim)
{
timer.Dispose(manualResetEventSlim.WaitHandle);
manualResetEventSlim.Wait();
}
Solution
Well the title is a bit "bold" i guess, but below is my attempt to deal with the issue - a wrapper which handles double-disposal, timeouts, and ObjectDisposedException. It does not provide all of the methods on Timer though - but feel free to add them.
internal class Timer
{
private readonly TimeSpan _disposalTimeout;
private readonly System.Threading.Timer _timer;
private bool _disposeEnded;
public Timer(TimeSpan disposalTimeout)
{
_disposalTimeout = disposalTimeout;
_timer = new System.Threading.Timer(HandleTimerElapsed);
}
public event Signal Elapsed;
public void TriggerOnceIn(TimeSpan time)
{
try
{
_timer.Change(time, Timeout.InfiniteTimeSpan);
}
catch (ObjectDisposedException)
{
// race condition with Dispose can cause trigger to be called when underlying
// timer is being disposed - and a change will fail in this case.
// see
// https://msdn.microsoft.com/en-us/library/b97tkt95(v=vs.110).aspx#Anchor_2
if (_disposeEnded)
{
// we still want to throw the exception in case someone really tries
// to change the timer after disposal has finished
// of course there's a slight race condition here where we might not
// throw even though disposal is already done.
// since the offending code would most likely already be "failing"
// unreliably i personally can live with increasing the
// "unreliable failure" time-window slightly
throw;
}
}
}
private void HandleTimerElapsed(object state)
{
Elapsed.SafeInvoke();
}
public void Dispose()
{
using (var waitHandle = new ManualResetEvent(false))
{
// returns false on second dispose
if (_timer.Dispose(waitHandle))
{
if (!waitHandle.WaitOne(_disposalTimeout))
{
throw new TimeoutException(
"Timeout waiting for timer to stop. (...)");
}
_disposeEnded = true;
}
}
}
}
Why you need to dispose the Timer manually? Isn't there any other
solution. As a rule of thumb, you're better leaving this job to GAC. –
LMB 56 mins ago
I am developing an ASP.NET application. The timer is disposed on the call of Dispose of the HttpApplication. The reason: A callback
could access the logging system. So i have to assure the before
disposing the logging system the timer is disposed. – SACO 50 mins ago
It looks like you have a Producer/Consumer pattern, using the timer as Porducer.
What I'd do in this case, would be to create a ConcurrentQueue() and make the timer enqueue jobs to the queue. And then, use another safe thread to read and execute the jobs.
This would prevent a job from overlapping another, which seems to be a requirement in your code, and also solve the timer disposing problem, since you could yourQueue == null before adding jobs.
This is the best design.
Another simple, but not robust, solution, is running the callbacks in a try block. I don't recommend to dispose the Timer manually.
Related
I have a service that subscribes to updates to a repository.
When an update message is received, the service needs to reload some data from the repository.
However many update messages can be received in a short period of time. So I want to create a buffer / time window, that will mean only one reload will happen for that period were many update messages arrived.
I've created a very rough outline:
class TestService
{
private Timer scheduledReloadTimer;
public void AttemptReload()
{
if (scheduledReloadTimer == null)
{
Console.WriteLine("Scheduling reload...");
scheduledReloadTimer = new Timer(Reload, null, 10000, Timeout.Infinite);
}
else
{
Console.WriteLine("Reload already scheduled for this period...");
}
}
private void Reload(object stateInfo)
{
scheduledReloadTimer.Dispose();
scheduledReloadTimer = null;
Console.WriteLine("Doing reload..");
}
}
Is using the null check on the Timer good enough to see if a reload has already been scheduled?
Am I disposing the Timer correctly?
Is there anything else I am missing here, especially around thread safety?
I've seen another stackoverflow answer that suggests using the Reactive Extensions to achieve this: https://stackoverflow.com/a/42887221/67357 but is that overkill?
You do have a potential thread-safety issue here. A quick fix would be to create a thread lock scope around the critical parts of your code, to ensure that while you're inspecting/creating and setting the timer variable, no other thread can get in there and start the same process in parallel:
class TestService
{
private Timer scheduledReloadTimer;
private object timerLock = new object();
public void AttemptReload()
{
lock (timerLock)
{
if (scheduledReloadTimer == null)
{
Console.WriteLine("Scheduling reload...");
scheduledReloadTimer = new Timer(Reload, null, 10000, Timeout.Infinite);
}
else
{
Console.WriteLine("Reload already scheduled for this period...");
}
}
}
private void Reload(object stateInfo)
{
lock (timerLock)
{
scheduledReloadTimer.Dispose();
scheduledReloadTimer = null;
}
Console.WriteLine("Doing reload..");
}
}
Reactive Extensions are a good way to deal with this throttling issue - as the code is already written for you.
Another approach might be to modify the AttemptReload call to simply reset the interval on the timer (if the reloadTimer != null), essentially pushing back the invocation of the timer event with each subsequent call to AttemptReload.
That way, the timer will definitely not fire until after the last call to AttemptReload + 10,000 milliseconds.
So I'm working on a Task Scheduling program using a System.Timers.Timer object to wait between tasks. For using this timer I'm throwing it into a static (extension) method on instantiation and using it only there. My question is will it be properly disposed of at the conclusion of that static method or do I need to do something more? Here's what I'm working with.
public static void Use<T>(this T o, Action<object> work) => work(o);
private void RunLoop()
{
while (!Stop)
{
try
{
//Method 1
new System.Timers.Timer((task.NextRun - DateTime.Now).TotalMilliseconds).Use(timer => {
timer.Elapsed += (s, e) => {
new Thread(new ThreadStart(RunProc)) {
Name = task.Title,
IsBackground = true
}.Start();
};
timer.AutoReset = false;
timer.Start();
});
//Method 2
using (var timer = new System.Timers.Timer((task.NextRun - DateTime.Now).TotalMilliseconds))
{
timer.Elapsed += (s, e) =>
{
new Thread(new ThreadStart(RunProc))
{
Name = task.Title,
IsBackground = true
}.Start();
};
timer.AutoReset = false;
timer.Start();
}
} catch(SqlException ex)
{ //Omitted for size }
catch (Exception ex)
{ //Omitted for size }
}
}
Will method 1 properly dispose of my timer, or do I have to call dispose or deal with a using statement for GC to get it? From what I've been reading for most objects they will get GC'ed when they fall out of scope of that lambda call in Use. But apparently Timers are different in that they connect to some un-managed resources which can cause them to persist even when you would expect they'd get cleaned up, like say going out of scope.
Although it's not technically required, Timers should always be disposed properly (just like nearly everything that inherits from IDisposable). What will happen if you don't dispose the timers? They will build up until a garbage collection detects that they're no longer reachable and then they will be finalized (which should automatically do the same thing as disposing of them, but it isn't as efficient).
In your method 2, the timer will likely not ever fire because it will be disposed immediately after it is created (before the Elapsed delegate runs).
So, the question becomes, when should you dispose of the timer? Well, that depends on how it is used. If you're only using it to execute that little code block once, you can dispose of it at the end of that execution. If it's supposed to trigger more than once, you'll need to have some other outside owner of the timer that disposes of it when it is no longer needed.
For closure, I ended up creating a list of timers for each action, and then just update the intervals, and recycle them as needed. Then added a specific function to properly stop and dispose of all of them, when stopping the service. The setup I have above fails once there are 2 tasks scheduled for the same time, so it wouldn't have worked either way. With this new way, I don't have to worry about it as they're either recycled indefinitely or specifically disposed of.
Well I've searched a lot for a solution to this. I'm looking for a clean and simple way to prevent the callback method of a System.Threading.Timer from being invoked after I've stopped it.
I can't seem to find any, and this has led me, on occassion, to resort to the dreaded thread-thread.sleep-thread.abort combo.
Can it be done using lock?
An easier solution might to be to set the Timer never to resume; the method Timer.Change can take values for dueTime and period that instruct the timer never to restart:
this.Timer.Change(Timeout.Infinite, Timeout.Infinite);
Whilst changing to use System.Timers.Timer might be a "better" solution, there are always going to be times when that's not practical; just using Timeout.Infinite should suffice.
like Conrad Frix suggested you should use the System.Timers.Timer class instead, like:
private System.Timers.Timer _timer = new System.Timers.Timer();
private volatile bool _requestStop = false;
public constructor()
{
_timer.Interval = 100;
_timer.Elapsed += OnTimerElapsed;
_timer.AutoReset = false;
_timer.Start();
}
private void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// do work....
if (!_requestStop)
{
_timer.Start();//restart the timer
}
}
private void Stop()
{
_requestStop = true;
_timer.Stop();
}
private void Start()
{
_requestStop = false;
_timer.Start();
}
The MSDN Docs suggest that you use the Dispose(WaitHandle) method to stop the timer + be informed when callbacks will no longer be invoked.
For the System.Threading.Timer one can do the following (Will also protect the callback-method from working on a disposed timer - ObjectDisposedException):
class TimerHelper : IDisposable
{
private System.Threading.Timer _timer;
private readonly object _threadLock = new object();
public event Action<Timer,object> TimerEvent;
public void Start(TimeSpan timerInterval, bool triggerAtStart = false,
object state = null)
{
Stop();
_timer = new System.Threading.Timer(Timer_Elapsed, state,
System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
if (triggerAtStart)
{
_timer.Change(TimeSpan.FromTicks(0), timerInterval);
}
else
{
_timer.Change(timerInterval, timerInterval);
}
}
public void Stop(TimeSpan timeout = TimeSpan.FromMinutes(2))
{
// Wait for timer queue to be emptied, before we continue
// (Timer threads should have left the callback method given)
// - http://woowaabob.blogspot.dk/2010/05/properly-disposing-systemthreadingtimer.html
// - http://blogs.msdn.com/b/danielvl/archive/2011/02/18/disposing-system-threading-timer.aspx
lock (_threadLock)
{
if (_timer != null)
{
ManualResetEvent waitHandle = new ManualResetEvent(false)
if (_timer.Dispose(waitHandle))
{
// Timer has not been disposed by someone else
if (!waitHandle.WaitOne(timeout))
throw new TimeoutException("Timeout waiting for timer to stop");
}
waitHandle.Close(); // Only close if Dispose has completed succesful
_timer = null;
}
}
}
public void Dispose()
{
Stop();
TimerEvent = null;
}
void Timer_Elapsed(object state)
{
// Ensure that we don't have multiple timers active at the same time
// - Also prevents ObjectDisposedException when using Timer-object
// inside this method
// - Maybe consider to use _timer.Change(interval, Timeout.Infinite)
// (AutoReset = false)
if (Monitor.TryEnter(_threadLock))
{
try
{
if (_timer==null)
return;
Action<Timer, object> timerEvent = TimerEvent;
if (timerEvent != null)
{
timerEvent(_timer, state);
}
}
finally
{
Monitor.Exit(_threadLock);
}
}
}
}
This is how one can use it:
void StartTimer()
{
TimerHelper _timerHelper = new TimerHelper();
_timerHelper.TimerEvent += (timer,state) => Timer_Elapsed();
_timerHelper.Start(TimeSpan.FromSeconds(5));
System.Threading.Sleep(TimeSpan.FromSeconds(12));
_timerHelper.Stop();
}
void Timer_Elapsed()
{
// Do what you want to do
}
For what it's worth, we use this pattern quite a bit:
// set up timer
Timer timer = new Timer(...);
...
// stop timer
timer.Dispose();
timer = null;
...
// timer callback
{
if (timer != null)
{
..
}
}
This answer relates to System.Threading.Timer
I've read a lot of nonsense about how to synchronize disposal of System.Threading.Timer all over the net. So that's why I'm posting this in an attempt to rectify the situation somewhat. Feel free to tell me off / call me out if something I'm writing is wrong ;-)
Pitfalls
In my opinion there's these pitfalls:
Timer.Dispose(WaitHandle) can return false. It does so in case it's already been disposed (I had to look at the source code). In that case it won't set the WaitHandle - so don't wait on it!
not handling a WaitHandle timeout. Seriously - what are you waiting for in case you're not interested in a timeout?
Concurrency issue as mentioned here on msdn where an ObjectDisposedException can occur during (not after) disposal.
Timer.Dispose(WaitHandle) does not work properly with -Slim waithandles, or not as one would expect. For example, the following does not work (it blocks forever):
using(var manualResetEventSlim = new ManualResetEventSlim)
{
timer.Dispose(manualResetEventSlim.WaitHandle);
manualResetEventSlim.Wait();
}
Solution
Well the title is a bit "bold" i guess, but below is my attempt to deal with the issue - a wrapper which handles double-disposal, timeouts, and ObjectDisposedException. It does not provide all of the methods on Timer though - but feel free to add them.
internal class Timer
{
private readonly TimeSpan _disposalTimeout;
private readonly System.Threading.Timer _timer;
private bool _disposeEnded;
public Timer(TimeSpan disposalTimeout)
{
_disposalTimeout = disposalTimeout;
_timer = new System.Threading.Timer(HandleTimerElapsed);
}
public event Action Elapsed;
public void TriggerOnceIn(TimeSpan time)
{
try
{
_timer.Change(time, Timeout.InfiniteTimeSpan);
}
catch (ObjectDisposedException)
{
// race condition with Dispose can cause trigger to be called when underlying
// timer is being disposed - and a change will fail in this case.
// see
// https://msdn.microsoft.com/en-us/library/b97tkt95(v=vs.110).aspx#Anchor_2
if (_disposeEnded)
{
// we still want to throw the exception in case someone really tries
// to change the timer after disposal has finished
// of course there's a slight race condition here where we might not
// throw even though disposal is already done.
// since the offending code would most likely already be "failing"
// unreliably i personally can live with increasing the
// "unreliable failure" time-window slightly
throw;
}
}
}
private void HandleTimerElapsed(object state)
{
Elapsed?.Invoke();
}
public void Dispose()
{
var waitHandle = new ManualResetEvent(false));
// returns false on second dispose
if (_timer.Dispose(waitHandle))
{
if (waitHandle.WaitOne(_disposalTimeout))
{
_disposeEnded = true;
waitHandle.Dispose();
}
else
{
// don't dispose the wait handle, because the timer might still use it.
// Disposing it might cause an ObjectDisposedException on
// the timer thread - whereas not disposing it will
// result in the GC cleaning up the resources later
throw new TimeoutException(
"Timeout waiting for timer to stop. (...)");
}
}
}
}
You can't guarantee that your code that supposed to stop the timer will execute before timer event invocation.
For example, suppose on time moment 0 you initialized timer to call event when time moment 5 comes. Then on time moment 3 you decided that you no longer needed the call. And called method you want to write here. Then while method was JIT-ted comes time moment 4 and OS decides that your thread exhaust its time slice and switch. And timer will invoke the event no matter how you try - your code just won't have a chance to run in worst case scenario.
That's why it is safer to provide some logic in the event handler. Maybe some ManualResetEvent that will be Reset as soon as you no longer needed event invocation. So you Dispose the timer, and then set the ManualResetEvent. And in the timer event handler first thing you do is test ManualResetEvent. If it is in reset state - just return immediately. Thus you can effectively guard against undesired execution of some code.
To me, this seems to be the correct way to go:
Just call dispose when you are done with the timer. That will stop the timer and prevent future scheduled calls.
See example below.
class Program
{
static void Main(string[] args)
{
WriteOneEverySecond w = new WriteOneEverySecond();
w.ScheduleInBackground();
Console.ReadKey();
w.StopTimer();
Console.ReadKey();
}
}
class WriteOneEverySecond
{
private Timer myTimer;
public void StopTimer()
{
myTimer.Dispose();
myTimer = null;
}
public void ScheduleInBackground()
{
myTimer = new Timer(RunJob, null, 1000, 1000);
}
public void RunJob(object state)
{
Console.WriteLine("Timer Fired at: " + DateTime.Now);
}
}
Perhaps you should do the opposite. Use system.timers.timer, set the AutoReset to false and only Start it when you want to
You can stop a timer by creating a class like this and calling it from, for example, your callback method:
public class InvalidWaitHandle : WaitHandle
{
public IntPtr Handle
{
get { return InvalidHandle; }
set { throw new InvalidOperationException(); }
}
}
Instantiating timer:
_t = new Timer(DisplayTimerCallback, TBlockTimerDisplay, 0, 1000);
Then inside callback method:
if (_secondsElapsed > 80)
{
_t.Dispose(new InvalidWaitHandle());
}
There is a MSDN link how to achieve stop timer correctly. Use ControlThreadProc() method with HandleElapsed(object sender, ElapsedEventArgs e) event synchronized by syncPoint static class variable. Comment out Thread.Sleep(testRunsFor); on ControlThreadProc() if it is not suitable(probably).
The key is there that using static variable and an atomic operation like Interlocked.CompareExchange on conditional statements.
Link :
Timer.Stop Method
In what circumstances would updating a UI control from a non-UI thread could cause the processes' handles to continually increase, when using a delegate and .InvokeRequired?
For example:
public delegate void DelegateUIUpdate();
private void UIUpdate()
{
if (someControl.InvokeRequired)
{
someControl.Invoke(new DelegateUIUpdate(UIUpdate));
return;
}
// do something with someControl
}
When this is called in a loop or on timer intervals, the handles for the program consistently increase.
EDIT:
If the above is commented out and amended as such:
public delegate void DelegateUIUpdate();
private void UIUpdate()
{
//if (someControl.InvokeRequired)
//{
// someControl.Invoke(new DelegateUIUpdate(UIUpdate));
// return;
//}
CheckForIllegalCrossThreadCalls = false;
// do something with someControl
}
...then the handles stop incrementing, however I don't want to allow cross thread calls, of course.
EDIT 2:
Here is a sample that shows the handles increase:
Thread thread;
private delegate void UpdateGUI();
bool UpdateTheGui = false;
public Form1()
{
InitializeComponent();
thread = new Thread(new ThreadStart(MyThreadLoop));
thread.Start();
}
private void MyThreadLoop()
{
while (true)
{
Thread.Sleep(500);
if (UpdateTheGui)
{
UpdateTheGui = false;
UpdateTheGuiNow();
}
}
}
private void UpdateTheGuiNow()
{
if (label1.InvokeRequired)
{
label1.Invoke(new UpdateGUI(UpdateTheGuiNow));
return;
}
label1.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
label2.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
label3.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
}
private void btnInvoke_Click(object sender, EventArgs e)
{
UpdateTheGui = true;
}
I had the same problem with
this.Invoke(new DelegateClockUpdate(ChangeClock), sender, e);
creating one handle each call.
The handle increments because Invoke is Synchronous and effectively the handle has been left hanging.
Either a Wait Handle should be used to process the result or the Asynchronous BeginInvoke method as shown below.
this.BeginInvoke(new DelegateClockUpdate(ChangeClock), sender, e);
The Control.Invoke() method doesn't consume any handles. However, this code is clearly called from a thread. A Thread does consume handles, 5 of them.
The Thread class doesn't have a Dispose() method, although it ought to have one. That was probably by design, it would be very difficult to call reliably, impossibly so for threadpool threads. The 5 handles that a thread requires are released by the finalizer. Your program will require ever increasing amounts of handles if the finalizer never runs.
Not getting the finalizer to run is quite unusual. You would have to have a program that starts a lot of threads but doesn't allocate a lot of memory. This tends to only happen in static tests. You can diagnose this condition with Perfmon.exe, use the .NET memory performance counters and check if gen #0 collections are being done.
If this happens in a production program then you'll have to call GC.Collect() yourself to avoid a runaway handle leak.
I've seen the same thing in my code. I fixed it by replacing Invoke with BeginInvoke. The handle leak went away.
Doron.
I actually see the same problem occuring as JYelton. I have the same call from within a thread to update the UI.
As soon as the line someControl.Invoke(new DelegateUIUpdate(UIUpdate)); is called, the handle increases by one. There is certainly a leak of some kind on the invoke, but I have no idea what is causing it. This has been verified on several systems.
Aync call with explicit handle finalize. Exapmle:
public static class ActionExtensions
{
private static readonly ILog log = LogManager.GetLogger(typeof(ActionExtensions));
/// <summary>
/// Async exec action.
/// </summary>
/// <param name="action">Action.</param>
public static void AsyncInvokeHandlers(
this Action action)
{
if (action == null)
{
return;
}
foreach (Action handler in action.GetInvocationList())
{
// Initiate the asychronous call. Include an AsyncCallback
// delegate representing the callback method, and the data
// needed to call EndInvoke.
handler.BeginInvoke(
ar =>
{
try
{
// Retrieve the delegate.
var handlerToFinalize = (Action)ar.AsyncState;
// Call EndInvoke to free resources.
handlerToFinalize.EndInvoke(ar);
var handle = ar.AsyncWaitHandle;
if (handle.SafeWaitHandle != null && !handle.SafeWaitHandle.IsInvalid && !handle.SafeWaitHandle.IsClosed)
{
((IDisposable)handle).Dispose();
}
}
catch (Exception exception)
{
log.Error("Async Action exec error.", exception);
}
},
handler);
}
}
}
See http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle.aspx note:
When you use the BeginInvoke method of a delegate to call a method asynchronously and obtain a wait handle from the resulting IAsyncResult, we recommend that you close the wait handle as soon as you are finished using it, by calling the WaitHandle.Close method. If you simply release all references to the wait handle, system resources are freed when garbage collection reclaims the wait handle, but garbage collection works more efficiently when disposable objects are explicitly closed or disposed. For more information, see the AsyncResult.AsyncWaitHandle property.
Here's an extension method which functions similarly to the normal Invoke call, but will clean up the handle after:
namespace ExtensionMethods
{
public static class ExtensionMethods
{
public static void InvokeAndClose(this Control self, MethodInvoker func)
{
IAsyncResult result = self.BeginInvoke(func);
self.EndInvoke(result);
result.AsyncWaitHandle.Close();
}
}
}
You can then call it very similarly to a normal invoke:
myForm.InvokeAndClose((MethodInvoker)delegate
{
someControl.Text = "New Value";
});
It will block and wait for the delegate to execute, then close the handle before returning.
This is the standard pattern for using Invoke to marshall updates to the UI thread.
Are you sure your problem is not being caused by other code in your application that is not included in your question?
I don't think it is related. Perhaps just waiting for the garbage collector to dispose the newly allocated object(s) inside Invoke().
I used this pattern in a few projects, (this snipped of code is from CodeCampServer), I understand what it does, but I'm really interesting in an explanation about this pattern. Specifically:
Why is the double check of _dependenciesRegistered.
Why to use lock (Lock){}.
Thanks.
public class DependencyRegistrarModule : IHttpModule
{
private static bool _dependenciesRegistered;
private static readonly object Lock = new object();
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
}
public void Dispose() { }
private static void context_BeginRequest(object sender, EventArgs e)
{
EnsureDependenciesRegistered();
}
private static void EnsureDependenciesRegistered()
{
if (!_dependenciesRegistered)
{
lock (Lock)
{
if (!_dependenciesRegistered)
{
new DependencyRegistrar().ConfigureOnStartup();
_dependenciesRegistered = true;
}
}
}
}
}
This is the Double-checked locking pattern.
The lock statement ensures that the code inside the block will not run on two threads simultaneously.
Since a lock statement is somewhat expensive, the code checks whether it's already been initialized before entering the lock.
However, because a different thread might have initialized it just after the outer check, it needs to check again inside the lock.
Note that this is not the best way to do it.
The double-check is because two threads could hit EnsureDependenciesRegistered at the same time, both find it isn't registered, and thus both attempt to get the lock.
lock(Lock) is essentially a form of mutex; only one thread can have the lock - the other must wait until the lock is released (at the end of the lock(...) {...} statement).
So in this scenario, a thread might (although unlikely) have been the second thread into the lock - so each must double-check in case it was the second, and the work has already been done.
It's a matter of performance.
The initial test lets it bail out quickly if the job is already done. At this point it does the potentially expensive lock but it has to check it again as another thread could have already registered it.
The double checked locking pattern is roughly:
you have an operation that you want to conditionally perform once
if (needsToDoSomething) {
DoSomething();
needsToDoSomething = false;
}
however, if you're running on two threads, both threads might check the flag, and perform the action, before they both set the flag to false. Therefore, you add a lock.
lock (Lock) {
if (needsToDoSomething) {
DoSomething();
needsToDoSomething = false;
}
}
however, taking a lock every time you run this code might be slow, so you decide, lets only try to take the lock when we actually need to.
if (needsToDoSomething)
lock (Lock) {
if (needsToDoSomething) {
DoSomething();
needsToDoSomething = false;
}
}
You can't remove the inner check, because once again, you have the problem that any check performed outside of a lock can possibly turn out to be true twice on two different threads.
The lock prevents two threads from running ConfigureOnStartup(). Between the if (!_dependenciesRegistered) and the point that ConfigureOnStartup() sets _dependenciesRegistered = true, another thread could check if it's registered. In other words:
Thread 1: _dependenciesRegistered == false
Thread 2: _dependenciesRegistered == false
Thread 1: ConfigureOnStartup() / _dependenciesRegistered = true;
Thread 2: Doesn't "see" that it's already registered, so runs ConfigureOnStartup() again.