BlockingCollection包含占用大量内存的项目.我想让生产者一次从BlockingCollection中取出一个项目并进行处理.
我想通过在BlockingCollection.GetConsumingEnumerable()上使用foreach,每次,BlockingCollection将从底层队列中删除该项(这意味着所有与引用一起),因此在Process()方法的末尾处理项目,该项目可以被垃圾收集.
但是这是错误的.似乎BlockingCollection.GetConsumingEnumerable()上的foreach循环确实包含输入到队列中的项的所有引用.所有物品都被保持(因此防止被垃圾收集)直到踩出foreach循环.
我没有在BlockingCollection.GetConsumingEnumerable()上使用简单的foreach循环,而是使用while循环测试BlockingCollection.IsComplete标志并在循环内部使用BlockingCollection.Take()来获取耗材.我假设BlockingCollection.Take()具有与List.Remove()类似的效果,它将从BlockingCollection中删除该项的引用.但这又是错误的.所有项目仅在while循环之外收集垃圾.
所以我的问题是,我们如何轻松实现这一要求,以便BlockingCollection可能会占用内存消耗的项目,并且一旦消费者使用了每个项目就可以进行垃圾回收?非常感谢您的帮助.
// 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."); }
输出是所有创建和处理消息,然后是“完成处理”.然后是来自实体的所有销毁消息.并且创建实体消息显示从0到9的Entity.ID以及显示从9到0的Entity.ID的销毁消息.
编辑:
即使我设置了BlockingCollection的绑定容量,所有进入它的项目只有在循环退出时才会终止,这很奇怪.