I have a bunch of concurrent threads carrying out operations which return alerts. The alerts have to be persisted to a DB, but this cannot be done concurrently as it may cause duplicate alerts being created.
To avoid duplicate alerts I have written a class containing a ConcurrentBag<T>
with a Timer
that triggers Action<T>
(in this case, sending my pending alerts to the DB) at a set interval.
I'm wondering if what I wrote it is written in a way that will not cause problems in the execution in the future. If alerts get lost due to an unexpected exception that would be problematic. I'd appreciate comments/criticisms/suggestions for improvement.
Here's the code:
class PeriodicConcurrentBag<T>
{
public int Count
{
get { return this.items.Count(); }
}
public bool HasItems
{
get { return this.items.Any(); }
}
public bool IsStopped { get; private set; }
private int timerPeriod;
private Timer timer;
private ConcurrentBag<T> items;
private Action<IEnumerable<T>> actionOnItems;
public PeriodicConcurrentBag(Action<IEnumerable<T>> actionOnItems, int periodInSeconds)
{
this.timerPeriod = periodInSeconds * 1000;
this.actionOnItems = actionOnItems;
this.items = new ConcurrentBag<T>();
}
public void Start()
{
this.IsStopped = false;
this.timer = new Timer(this.InternalPeriodicAction, null, this.timerPeriod, Timeout.Infinite);
}
public void Stop()
{
this.IsStopped = true;
}
public void AddItem(T item)
{
this.items.Add(item);
}
public void AddItems(IEnumerable<T> items)
{
this.items.AddRange(items);
}
private void InternalPeriodicAction(object state)
{
if (!this.IsStopped)
{
var currentItems = Interlocked.Exchange(ref this.items, new ConcurrentBag<T>());
this.actionOnItems(currentItems);
if (!this.IsStopped)
this.Start();
}
}
}
EDIT: The AddRange()
method is an extension method I created for ConcurrentBag
.