代码之家  ›  专栏  ›  技术社区  ›  Xaqron

多线程中的变量范围,为什么我的对象引用丢失?

  •  0
  • Xaqron  · 技术社区  · 14 年前

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

    1 回复  |  直到 14 年前
        1
  •  2
  •   Hans Passant    14 年前
    public bool Get(byte[] buffer)
    

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

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

    推荐文章