0

简单来说,在单生产者-单消费者场景下,我使用了一个可变对象来实现生产者和消费者之间的同步和传递数据和消息。共享缓冲区是一个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()方法经常创建新的缓冲区,虽然使用的缓冲区存储在回收站中,但回收站计数有时不会增加!

4

1 回答 1

2
public bool Get(byte[] buffer)

那将是一个明显的丢失参考的地方。此方法实际上无法返回检索到的缓冲区。您必须使用ref关键字让它返回数组。很难相信真正的代码看起来像这样,它根本不起作用。还有很多其他危险信号,ConcurrentBag 具有线程关联性,如果你动态创建消费者线程,东西就会丢失。您不能使用 ManualResetEvent 将消费者与生产者同步,它只能计数到 1。

通常,除非缓冲区大于 85KB,否则这种优化是不合适的。相信垃圾收集器,它做得非常好,很难改进。

于 2010-10-25T21:02:44.050 回答