I need to write a class to batch Order
objects from 3 calling components running on 3 separate threads. The 3 calling components will call the Batcher
class at the same time (within a second of each other), but to handle clock issues and thread interleaving, the method should wait for a maximum of 5 seconds for the 3 calls. If for any reason only 2 calls make it, the batcher should continue after the 5-second timeout and batch what it has. The Batcher
class must appear synchronized to the calling components.
I have written a class and it seems to work but I'm new to this and I'd appreciate it if someone could review my class.
public sealed class Batcher
{
private readonly object _syncLock = new object();
private readonly Timer _timer;
private bool _timerSet = false;
private volatile int _callCount = 0;
private const int ExecuteImmediatelyCallCount = 3;
private readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
private readonly List<Order> _orders = new List<Order>();
public Batcher()
{
_timer = new Timer(Batch, null, Timeout.Infinite, Timeout.Infinite);
}
public void TryBatch(IEnumerable<Order> orders)
{
lock (_syncLock)
{
_orders.AddRange(orders);
if(!_timerSet)
{
_timerSet = true;
_timer.Change(5000, Timeout.Infinite);
}
_callCount ++;
}
if(_callCount >= ExecuteImmediatelyCallCount)
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
Batch(null);
}
_manualResetEvent.WaitOne();
}
private void Batch(object state)
{
lock (_syncLock)
{
if(_orders.Count > 0)
{
RemoteService.Send(_orders);
_orders.Clear();
_manualResetEvent.Reset();
}
_callCount = 0;
}
}
)
-
\$\begingroup\$ Take a look at the tasks parallel Library \$\endgroup\$sll– sll2011年11月17日 16:16:06 +00:00Commented Nov 17, 2011 at 16:16
1 Answer 1
Not sure what is behind the requirement of 5 seconds, but from the top level (please comment to answer the items marked as warnings):
public sealed class Batcher
{
private readonly object _syncLock = new object();
private readonly Timer _timer;
private bool _timerSet = false;
private volatile int _callCount = 0;
private const int ExecuteImmediatelyCallCount = 3;
private readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
private readonly List<Order> _orders = new List<Order>();
public Batcher()
{
_timer = new Timer(Batch, null, Timeout.Infinite, Timeout.Infinite);
}
#warning What is the "orders" parameter for
public void TryBatch(IEnumerable<Order> orders)
{
#warning don't see a point to lock this operation
lock (_syncLock)
{
_orders.AddRange(orders);
if(!_timerSet)
{
_timerSet = true;
_timer.Change(5000, Timeout.Infinite);
}
_callCount ++;
}
#warning not thread safe
if(_callCount >= ExecuteImmediatelyCallCount)
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
Batch(null);
}
#warning what is this for?
_manualResetEvent.WaitOne();
}
private void Batch(object state)
{
lock (_syncLock)
{
if(_orders.Count > 0)
{
#warning not thread safe
RemoteService.Send(_orders);
_orders.Clear();
_manualResetEvent.Reset();
}
_callCount = 0;
}
}
)