简单地说,在单个生产者 - 单个消费者场景下,我使用了一个可变对象进行同步,并在生产者和消费者之间传递数据和消息。共享缓冲区是ConcurrentQueue
字节数组。为了实现循环缓冲区并防止堆碎片和GC
频繁的对象交换,我使用ConcurrentBag
字节数组作为使用的字节数组的循环二进制文件。 ManualResetEventSlim
用于线程同步。有时我在代码中丢失了对字节数组的引用。下面是我的代码的简化版本,以防您需要更多详细信息,但我想这是使用线程时的常规错误。
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);
}
}
}
实际上GetEmptyBuffer()
方法经常创建新缓冲区,虽然使用的缓冲区存储在recycle-bin中,但回收站计数有时不会提高!
答案 0 :(得分:2)
public bool Get(byte[] buffer)
这将是一个失去参考的明显地方。此方法实际上无法返回检索到的缓冲区。您必须使用 ref 关键字让它返回数组。很难相信真正的代码看起来像这样,它根本不起作用。还有很多其他的红旗,ConcurrentBag有线程关联性,如果你动态创建消费者线程,东西就会丢失。您不能使用ManualResetEvent将使用者与生产者同步,它只能计为1。
通常,除非缓冲区大于85KB,否则此优化是不合适的。相信垃圾收集器,它做得很好,非常难以改进。