patterncsharpMinor
Buffering of fast changing datapoints or events
Viewed 0 times
fasteventsdatapointsbufferingchanging
Problem
The following class implements a buffer which captures fast changing datapoints or frequently raised events and dispatches them in batches. It will asynchronously call a processing function for the buffered items under any of the following conditions:
There are several buffering strategies available which control how the items are accumulated.
It was written in times of .NET 3.5 and still needs to work with it.
Looking for general improvements, potential pitfalls I missed, design improvements etc.
A couple if things which annoy me but I never got around changing:
Code:
```
public class AccumulationBuffer : IDisposable
{
public enum RecordingStrategy
{
AllFifo,
AllLifo,
FirstWins,
LastWins
}
private interface IStorage : IEnumerable>
{
void Add(KeyValuePair pair);
void Clear();
}
private class StorageAllFifo : Queue>, IStorage
{
public StorageAllFifo(int capacity)
: base(capacity)
{
}
public void Add(KeyValuePair pair)
{
Enqueue(pair);
}
}
private class StorageAllLifo : Stack>, IStorage
{
public StorageAllLifo(int capacity)
: base(capacity)
{
}
public void Add(KeyValuePair pair)
{
Push(pair);
}
}
private class StorageMap : Dictionary, ISt
- A configurable timeout has expired since the last time an item was added
- A configurable maximum number of items was added
- The buffer is being disposed
There are several buffering strategies available which control how the items are accumulated.
It was written in times of .NET 3.5 and still needs to work with it.
Looking for general improvements, potential pitfalls I missed, design improvements etc.
A couple if things which annoy me but I never got around changing:
- Violation of open/closed principle in regards of the accumulation strategies. Hasn't been an issue so far but should be fixed at some point I guess.
- Using a dedicated processing thread. Putting the main method on the thread pool is not really an option because it's long running. Doing it on a timer callback would be nice but it's a bit tricky with the different signals which can trigger a dispatch of the buffered items.
Code:
```
public class AccumulationBuffer : IDisposable
{
public enum RecordingStrategy
{
AllFifo,
AllLifo,
FirstWins,
LastWins
}
private interface IStorage : IEnumerable>
{
void Add(KeyValuePair pair);
void Clear();
}
private class StorageAllFifo : Queue>, IStorage
{
public StorageAllFifo(int capacity)
: base(capacity)
{
}
public void Add(KeyValuePair pair)
{
Enqueue(pair);
}
}
private class StorageAllLifo : Stack>, IStorage
{
public StorageAllLifo(int capacity)
: base(capacity)
{
}
public void Add(KeyValuePair pair)
{
Push(pair);
}
}
private class StorageMap : Dictionary, ISt
Solution
Also, there isn't much to say (you have already mentioned the flaws) some nitpickings
-
You should be consistent in your coding style
Sometimes you are using braces
-
I would extract the retrieving of the "timeout TimeSpan" into a separate method,
and refactor the
-
The constructor of
-
You should be consistent in your coding style
if (_CurrentAccumulationCount >= _MaximumAccumulationCount)
break;
if (_LastAddedTime != DateTime.MinValue)
{
wait = _InactivityTime;
}Sometimes you are using braces
{} for single if statements (which i preffer) and sometimes not. -
I would extract the retrieving of the "timeout TimeSpan" into a separate method,
private TimeSpan GetWaitingTime()
{
lock (_StorageLock)
{
if(_CurrentAccumulationCount >= _MaximumAccumulationCount)
{
return TimeSpan.Zero;
}
if (_LastAddedTime != DateTime.MinValue)
{
return _InactivityTime;
}
}
return TimeSpan.FromMilliseconds(-1);
}and refactor the
while (!timeout && !_Quit) loop to bool hasTimedOut = false;
while (!hasTimedOut && !_Quit)
{
TimeSpan waitingTime = GetWaitingTime();
if(waitingTime == TimeSpan.Zero)
{
break;
}
hasTimedOut = !_MonitorEvent.WaitOne(waitingTime); // WaitOne returns false if it timed out
}-
The constructor of
StorageAllFifo and StorageAllLifo can be removed, as the base constructor is called anyway.Code Snippets
if (_CurrentAccumulationCount >= _MaximumAccumulationCount)
break;
if (_LastAddedTime != DateTime.MinValue)
{
wait = _InactivityTime;
}private TimeSpan GetWaitingTime()
{
lock (_StorageLock)
{
if(_CurrentAccumulationCount >= _MaximumAccumulationCount)
{
return TimeSpan.Zero;
}
if (_LastAddedTime != DateTime.MinValue)
{
return _InactivityTime;
}
}
return TimeSpan.FromMilliseconds(-1);
}bool hasTimedOut = false;
while (!hasTimedOut && !_Quit)
{
TimeSpan waitingTime = GetWaitingTime();
if(waitingTime == TimeSpan.Zero)
{
break;
}
hasTimedOut = !_MonitorEvent.WaitOne(waitingTime); // WaitOne returns false if it timed out
}Context
StackExchange Code Review Q#66918, answer score: 4
Revisions (0)
No revisions yet.