|
using System;
using System.Threading;
namespace Pfz.Threading
{
/// <summary>
/// Class that creates a Timer that runs an action at given intervals.
/// But if the operation takes too long, it will simple let the time pass and will start the next action
/// as soon as the other one finishes but never stacks or run them in parallel.
/// </summary>
public sealed class UnsafeNonStackingTimer:
IAdvancedDisposable
{
private PooledManualResetEvent _manualResetEvent;
private PooledAutoResetEvent _disposed;
/// <summary>
/// Creates a new timer that will run the given action at the given interval milliseconds.
/// If exclusiveThreadName is not null, then a new Thread will be created. Otherwise, a
/// UnlimitedThreadPool thread will be used.
/// </summary>
public UnsafeNonStackingTimer(Action action, int interval, bool runImmediatelly=false, string exclusiveThreadName=null)
{
if (action == null)
throw new ArgumentNullException("action");
if (interval < 1)
throw new ArgumentOutOfRangeException("interval must be at least 1 millisecond.");
Action = action;
Interval = interval;
try
{
_manualResetEvent = new PooledManualResetEvent(false);
if (exclusiveThreadName == null)
UnlimitedThreadPool.Run(_Run, (object)runImmediatelly);
else
{
Thread thread = new Thread(_Run);
thread.Name = exclusiveThreadName;
thread.Start(runImmediatelly);
}
}
catch
{
var manualResetEvent = _manualResetEvent;
if (manualResetEvent != null)
{
_manualResetEvent = null;
manualResetEvent.Dispose();
}
throw;
}
}
/// <summary>
/// Releases the Timer thread immediatelly.
/// Note that, if such thread is already running, it will continue to the end of the action,
/// and only then will be returned to the pool, but the Timer will return immediatelly.
/// </summary>
public void Dispose()
{
var manualResetEvent = _manualResetEvent;
if (manualResetEvent != null)
manualResetEvent.Set();
}
/// <summary>
/// Disposes this timer and waits until the last action is finished (if there is one).
/// </summary>
public void DisposeAndWait()
{
lock(_manualResetEvent)
_disposed = new PooledAutoResetEvent(false);
_manualResetEvent.Set();
_disposed.WaitOne();
_disposed.Dispose();
}
/// <summary>
/// Throws an exception if this object was already disposed.
/// </summary>
private void _CheckUndisposed()
{
if (WasDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
/// <summary>
/// Gets a value indicating if this object was already disposed.
/// </summary>
public bool WasDisposed
{
get
{
var manualResetEvent = _manualResetEvent;
try
{
return manualResetEvent == null || manualResetEvent.WaitOne(0);
}
catch
{
return true;
}
}
}
/// <summary>
/// Gets the action that's run by this timer.
/// </summary>
public Action Action { get; private set; }
/// <summary>
/// Gets the interval in which the action is run.
/// </summary>
public int Interval { get; private set; }
private void _Run(object runImmediatelly)
{
var manualResetEvent = _manualResetEvent;
var currentThread = Thread.CurrentThread;
try
{
if ((bool)runImmediatelly)
Action();
int nextInterval = Interval;
while(true)
{
currentThread.IsBackground = true;
if (manualResetEvent.WaitOne(nextInterval))
return;
currentThread.IsBackground = false;
DateTime begin = DateTime.Now;
Action();
DateTime end = DateTime.Now;
TimeSpan lastActionTime = end - begin;
if (lastActionTime <= TimeSpan.Zero)
nextInterval = Interval;
else
{
nextInterval = Interval - (int)lastActionTime.TotalMilliseconds;
if (nextInterval < 0)
nextInterval = 0;
}
}
}
finally
{
currentThread.IsBackground = false;
_manualResetEvent = null;
manualResetEvent.Dispose();
lock(manualResetEvent)
if (_disposed != null)
_disposed.Set();
}
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.
Want more info or simply want to contact me?
Take a look at:
http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com
Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).