2010-10-21 12 views
6

मैं BlockingCollection का उपयोग करता हूं ताकि सी # 4.0 में निर्माता-उपभोक्ता पैटर्न को कार्यान्वित किया जा सके।टास्क समांतर लाइब्रेरी में ब्लॉकिंग कोलेक्शन स्वचालित रूप से अंतर्निहित उदाहरणों का संदर्भ नहीं देता है

BlockingCollection ऐसी चीजें हैं जो काफी मेमोरी लेते हैं। मैं निर्माता को एक समय में ब्लॉकिंग कोलेक्शन से एक आइटम लेना चाहता हूं, और इसे संसाधित करना चाहता हूं।

मैं सोच रहा था BlockingCollection.GetConsumingEnumerable() पर हर बार foreach का उपयोग कर, द्वारा, BlockingCollection आइटम अंतर्निहित कतार से (सब एक साथ संदर्भ में इसका मतलब है कि) निकाल देंगे कि इतने विधि प्रक्रिया() जो आइटम प्रक्रियाओं के अंत में , आइटम कचरा एकत्र किया जा सकता है।

लेकिन यह सच नहीं है। ऐसा लगता है कि BlockingCollection.GetConsumingEnumerable() पर फ़ोरैच लूप कतार में दर्ज वस्तुओं के सभी संदर्भ रखता है। फोरैच लूप से बाहर निकलने तक सभी वस्तुओं को आयोजित किया जाता है (इस प्रकार कचरा इकट्ठा होने से रोका जाता है)।

इसके बजाय BlockingCollection.GetConsumingEnumerable() पर सरल foreach पाश का उपयोग करने का

, मैं थोड़ी देर के पाश परीक्षण BlockingCollection.IsComplete ध्वज का उपयोग करें और लूप के अंदर मैं BlockingCollection.Take() का प्रयोग कर एक उपभोज्य आइटम हड़पने के लिए। मुझे लगता है कि BlockingCollection.Take() का समान प्रभाव List.Remove() है, जो ब्लॉकिंग कोलेक्शन से आइटम का संदर्भ हटा देगा। लेकिन फिर यह गलत है। सभी आइटम केवल लूप के बाहर एकत्र कचरा हैं।

तो मेरा सवाल यह है कि, हम इस आवश्यकता को कैसे कार्यान्वित कर सकते हैं कि ब्लॉकिंग कोलेक्शन में स्मृति-उपभोग करने वाली वस्तुओं को संभावित रूप से रखा जाता है और प्रत्येक आइटम उपभोक्ता द्वारा उपभोग किए जाने पर कचरा इकट्ठा किया जा सकता है? किसी भी सहायता के लिए आपका बहुत - बहुत धन्यवाद।

संपादित करें: के रूप में अनुरोध, एक साधारण डेमो कोड जोड़ा जाता है:

// Entity is what we are going to process. 
// The finalizer will tell us when Entity is going to be garbage collected. 
class Entity 
{ 
    private static int counter_; 
    private int id_; 
    public int ID { get{ return id_; } } 
    public Entity() { id_ = counter++; } 
    ~Entity() { Console.WriteLine("Destroying entity {0}.", id_); } 
} 

... 

private BlockingCollection<Entity> jobQueue_ = new BlockingCollection<Entity>(); 
private List<Task> tasks_ = new List<Task>(); 

// This is the method to launch and wait for the tasks to finish the work. 
void Run() 
{ 
    tasks_.Add(Task.Factory.StartNew(ProduceEntity); 
    Console.WriteLine("Start processing."); 
    tasks_.Add(Task.Factory.StartNew(ConsumeEntity); 
    Task.WaitAll(tasks_.ToArray()); 
} 

// The producer creates Entity instances and add them to BlockingCollection. 
void ProduceEntity() 
{ 
    for(int i = 0; i < 10; i ++) // We are adding totally 10 entities. 
    { 
     var newEntity = new Entity(); 
     Console.WriteLine("Create entity {0}.", newEntity.ID); 
     jobQueue_.Add(newEntity); 
    } 
    jobQueue_.CompleteAdding(); 
} 

// The consumer takes entity, process it (and what I need: destroy it). 
void ConsumeEntity() 
{ 
    while(!jobQueue_.IsCompleted){ 
     Entity entity; 
     if(jobQueue_.TryTake(entity)) 
     { 
      Console.WriteLine("Process entity {0}.", entity.ID); 
      entity = null; 

      // I would assume after GC, the entity will be finalized and garbage collected, but NOT. 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      GC.Collect(); 
     } 
    } 
    Console.WriteLine("Finish processing."); 
} 

उत्पादन है कि सारी सृष्टि और इस प्रक्रिया संदेश, जिसके बाद "संसाधन समाप्त करें।" और संस्थाओं के सभी विनाश संदेशों के बाद। और निर्माण संस्थाओं Entity.ID दिखा संदेश 0 से 9 तक और विनाश 9 से 0.

संपादित करने के लिए Entity.ID दिखा संदेशों:

यहां तक ​​कि जब मैं BlockingCollection के लिए बाध्य क्षमता निर्धारित करते हैं, सभी आइटम कभी इसे में प्रवेश केवल तभी समाप्त हो जाते हैं जब लूप निकलता है, जो अजीब है।

+0

सिर्फ इसलिए कि वहाँ आयोजित किया जा रहा रेफरी नहीं है जीसी मतलब यह नहीं है में अभी कदम है और यह एकत्रित करेगा ... नमूना कोड है कि उपयुक्त GC.Collect आदि तरीकों के साथ अपने मुद्दे को दर्शाता है होगा सहायक रहें –

उत्तर

2

क्या ब्लॉकिंग कोलेक्शन संदर्भ जारी रखना जारी रखता है, जो संग्रह प्रकार पर निर्भर करता है।

BlockingCollection<T>ConcurrentQueue<T> के लिए default collection type है।

तो कचरा संग्रहण व्यवहार संग्रह प्रकार पर निर्भर करेगा। ConcurrentQueue<T के मामले में> यह एक फीफो संरचना है, इसलिए मुझे आश्चर्य होगा कि अगर उन्होंने कतार से हटा दिए जाने के बाद डेटा संरचना से संदर्भ जारी नहीं किए हैं (यह एक कतार की परिभाषा है)!

आप कैसे निर्धारित कर रहे हैं कि ऑब्जेक्ट्स कचरा नहीं जा रहे हैं?

6

ConcurrentQueue में 32 आइटमों की आंतरिक सरणी के साथ सेगमेंट शामिल हैं। सेगमेंट कचरा इकट्ठा होने तक इकाई वस्तुओं को कचरा नहीं बनाया जाएगा। कतार से सभी 32 वस्तुओं को निकालने के बाद यह घटित होगा। यदि आप 32 आइटम जोड़ने के लिए अपना उदाहरण बदलते हैं तो आपको "प्रक्रिया समाप्त करने" से पहले "नष्ट करने वाली इकाई" संदेश दिखाई देंगे।"

+0

एचएम। यह मेरे लिए एक अजीब व्यवहार है। मैं यह सुनिश्चित नहीं कर सकता कि हर बार हमारे पास बहुत सारी चीज़ें हों। लेकिन मैंने आपके द्वारा वर्णित कोशिश की है। मुझे लगता है कि मुझे हर बार मैन्युअल रूप से प्रत्येक आइटम द्वारा आयोजित स्मृति मुक्त करना होगा। एक वस्तु का उपभोग करें। आपका अंतिम जवाब के बहुत करीब है। लेकिन मैं यह देखने के लिए इंतजार करूँगा कि कोई और इस अजीब व्यवहार से निपटने के लिए समाधान के साथ आ सकता है या नहीं। – Steve

संबंधित मुद्दे

 संबंधित मुद्दे