2010-10-25 30 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