2010-10-25 54 views
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

如果我在哪里,我会担心堆碎片等,一旦我得到代码*工作*。 – aioobe 2010-10-25 19:16:17

+0

recylceBin何时清空? – 2010-10-25 19:28:05

+0

这段代码实际上工作吗?正如我所看到的,它不应该编译。 – 2010-10-25 19:33:08

回答

2
public bool Get(byte[] buffer) 

这将是一个明显的失去参考的地方。该方法实际上不能返回检索到的缓冲区。您将不得不使用ref关键字来让它返回数组。很难相信真正的代码看起来像这样,它根本不会工作。还有很多其他的红旗,ConcurrentBag具有线程关联性,如果您在运行中创建消费者线程,则东西会丢失。您无法使用ManualResetEvent与生产者同步消费者,它只能计为1.

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

+0

实际代码要复杂得多。我立即做了简短的介绍,在再次检查后发现签名是“public bool Get(out byte [] buffer)”,但它可能与out/ref的使用有关。 ConcurrentBag是线程安全的,因此用于保留引用。其实我用Monitor进行同步。缓冲区为1 MB,在性能测试中,我因频繁交换对象而出现红旗,因此改变了设计。如果你知道样品好,请与我分享。谢谢。 – Xaqron 2010-10-25 23:58:30

+0

你没有通过发布垃圾代码来帮助自己。然后不修复它。祝你好运。 – 2010-10-26 05:36:59