2012-04-02 2 views
3

Являются ли объекты, помещенные в ConcurrentQueue, копируются в очередь или только их ссылки?ConcurrentQueue содержит ссылку на объект или значение? исключение «из памяти»

Я не понимаю ни одного сценария.

Объяснение:

Я определил ConcurrentQueue так:

// BufferElement is a class I created 
private ConcurrentQueue<BufferElement> _bufferQueue; 

У меня есть функция, которая называется много раз, и это purpsoe является епдиеим элементом в очередь:

private void EnqueueElementToBuffer(string data, int moreData) 
{ 
    // the bufferElement constructor is setting data and moreData to it's fields. 
    BufferElement bufferElement = new BufferElement(data, moreData); 
    bufferQueue.Enqueue(bufferElement); 
} 

Когда я запускаю это, я получаю исключение из памяти через некоторое время. Я думал, что это может быть потому, что сборщик мусора не собирает bufferElement, потому что он до сих пор упоминается в bufferQueue, поэтому я изменил функцию к этому:

private void EnqueueElementToBuffer(string data, int moreData) 
{ 
    // _bufferElement is now a filed of the class 
    _bufferElement.Data = data; 
    _bufferElement.MoreData = moreData; 
    bufferQueue.Enqueue(_bufferElement); 
} 

И я не получаю исключение, и WASN 't собирается получить один судить по памяти в диспетчере задач Windows.

Теперь я думал, что проблема была решена, потому что, когда я помещал объекты в очередь, только ссылка на объекты привязана к очереди, но я боялся, что все элементы в очереди ссылаются на один и тот же объект, поэтому я проверил другую функцию у меня в другом потоке, который делает то, что он является:

// while bufferQueue is not empty do the following 
    BufferElement bufferElement = null; 
    bufferQueue.TryDequeue(out bufferElement); 

и я проверил содержание пары элементов и увидел, что их содержание было по-другому! поэтому, если объекты, помещенные в очередь, копируются по значению, почему я сначала получил исключение из памяти?

+0

Ответ во многом зависит от того, что такое BufferElement, класс или структура. –

+0

'BufferElement' - это класс – remi

ответ

3

Когда вы звоните Enqueue, только копия справки хранится в ConcurrentQueue<T>. Но эта ссылка сильно удерживается, что означает, что она сохраняет актуальный объект в памяти. Элемент не будет иметь права на инкассо, пока ссылка не будет удалена из ConcurrentQueue<T>

Причины вы не видите OutOfMemoryException, когда вы перешли на использование поля происходит потому, что вы в корне изменили семантику

  • Оригинальный код: Выдвинутый N ссылки на N элементов в очереди, следовательно, она держит N элементов в памяти
  • Изменен код: Выдвинутый N ссылки на 1 элемент в очереди, следовательно, он держит 1 элемент в памяти

Это значительно уменьшило объем памяти, который хранился в памяти объектный граф ConcurrentQueue<T> и предотвратил исключение.

Кажется, проблема в том, что вы просто выполняете операции намного быстрее, чем обрабатываете их. До тех пор, пока это будет справедливо, вы в конечном итоге исчерпаете память в приложении. Ваш код должен быть настроен таким образом, чтобы он не попадал в это состояние.

Примечание: Этот ответ написан в предположении, что BufferElement является class, а не struct.

Примечание2: Как указывает Servy в комментариях, вы можете рассмотреть возможность переключения на BlockingCollection<T>, поскольку у него есть несколько возможностей дросселирования, которые могут вам помочь.

+2

Вы можете использовать' BlockingCollection' (который используется внутри как параллельная очередь) и установить его емкость, чтобы при попытке добавить больше, чем элементы X, он блокируется до тех пор, пока не будет свободного места, которое может быть что вам нужно, чтобы замедлить работу ваших производителей и поддерживать их в соответствии с потребностями потребителей. – Servy

1

Чтобы ответить на первый вопрос, ConcurrentQueue<T> содержит ссылку на содержание. Это справедливо даже для значения типа T, потому что они помещаются в очередь после размещения в очереди.

Причина, по которой вы получили OOM для начала, вероятно, что вы либо никогда не деактивируете элементы (и, следовательно, да, очередь будет поддерживать ссылки на них и сохранять их в живых до тех пор, пока вы их не разберете), или вы не деактивируете их достаточно быстро по сравнению с тем, как быстро вы их ставите в очередь.

В вашем «исправлении» вы просто создаете только BufferElement и постоянно перезаписываете его поля. Вы снова и снова вставляете один и тот же экземпляр класса, что почти наверняка не является поведением, которое вы хотите.

+0

Неверно, что тип значения T будет помещен в коробку - они будут скопированы в 'ConcurrentQueue .Segment.m_array', но не будут помещены в бокс. – crimbo

Смежные вопросы