Briefly under a single producer - single consumer scenario, I used a mutable object for synchronization and passing data and messages between producer and consumer. Shared buffer is a ConcurrentQueue
of byte arrays. To implement a circular buffer and prevent heap fragmentation and frequent object swapping by GC
I used a ConcurrentBag
of byte arrays as a recycle-bin for used byte arrays. ManualResetEventSlim
is used for thread synchronization. Sometimes I lose reference to byte arrays in my code. Below is a simplified version of my code in case you need more details but I guess this is a routine mistake while working with threads.
MutableObject mutableObject = new MutableObject();
Producer producer = MutableObject.GetProducer();
Consumer consumer = MutableObject.GetConsumer();
Thread fork = new Thread(new ThreadStart(producer.Start));
// Forking execution path
fork.Start();
// Main thread goes here
consumer.Start();
class MutableObject()
{
private Producer m_producer;
private Consumer m_consumer;
private ConcurrentBag<byte[]> m_recycleBin = new ConcurrentBag<byte[]>();
private ConcurrentQueue<byte[]> m_sharedBuffer = new ConcurrentQueue<byte[]>();
public Producer GetProducer()
{
// Keep a reference to the mutable object
return new Producer(this);
}
// GetConsumer() method is just like GetProducer() method
public void GetEmptyBuffer(out byte[] buffer)
{
if (!m_recycleBin.TryTake(out buffer))
buffer = new byte[1024];
}
public bool Put(byte[] buffer)
{
m_sharedBuffer.Enqueue(buffer);
// Set ManualResetEventSlim for consumer
}
public bool Get(byte[] buffer) // Consumer calls this method in a loop
{
m_sharedBuffer.TryDequeue(out buffer);
// I save a reference to buffer here and pass it to recyclebin at next call like this: lastBuffer = buffer;
// This is because buffers are passing by refrence for I should wait until it would be used by consumer.
m_recycleBin.Add(lastBuffer);
// Set ManualResetEventSlim for producer
}
}
class Producer
{
private MutableObject m_mutableObject;
public Producer(MutableObject mutableObject)
{
m_mutableObject = mutableObject;
}
public void Start()
{
byte[] buffer;
while (true)
{
m_mutableObject.GetEmptyBuffer(out buffer);
m_mutableObject.Put(buffer);
}
}
}
Actually GetEmptyBuffer()
method frequently creates new buffers and although used buffers are stored in recycle-bin, the recycle-bin count doesn't raise sometimes !