Очередь <T>.Dequeue возвращает пустой указатель

У меня есть сценарий где

  • несколько потоков продвигают данные по Очереди

  • ТОЛЬКО ОДИН поток обрабатывает данные с помощью кода ниже

код -

  while ( Continue )
  {
        while ( queue.Count > 0 )
        {
             MyObj o = queue.Dequeue();
             someProcess(o);
        }
        myAutoResetEvent.WaitOne();
  }

Но иногда, очередь. Двухсторонняя очередь () возвращает пустой указатель в сценарии выше того, Что дает?

7
задан Kumar 12 February 2010 в 14:04
поделиться

7 ответов

Вам необходимо прочитать это сообщение в блоге .

Кроме того, вот очень минимальный каркас «канала» для связи между потоками:

public class Channel<T>
{
    private readonly Queue<T> _queue = new Queue<T>();

    public void Enqueue(T item)
    {
        lock (_queue)
        {
            _queue.Enqueue(item);
            if (_queue.Count == 1)
                Monitor.PulseAll(_queue);
        }
    }

    public T Dequeue()
    {
        lock (_queue)
        {
            while (_queue.Count == 0)
                Monitor.Wait(_queue);

            return _queue.Dequeue();
        }
    }
}
8
ответ дан 6 December 2019 в 05:55
поделиться

Вы говорите:

несколько потоков помещают данные в очередь

Метод Queue .Enqueue не является потокобезопасным. Это означает, что работа выполняется внутри метода Enqueue , который необходимо синхронизировать, если его вызывают несколько потоков. Простым примером может быть обновление свойства Count . Можно с уверенностью сказать, что где-то в методе Enqueue есть строка, которая выглядит примерно так:

++count;

Но, как мы все знаем, это не атомарная операция. На самом деле это больше похоже на это (с точки зрения того, что на самом деле происходит ):

int newCount = count + 1;
count = newCount;

Итак, предположим, что счетчик в настоящее время равен 5, а поток 1 проходит int newCount = count + 1 ... затем поток 1 думает: «Хорошо, счет сейчас 5, так что я сделаю 6». Но самая следующая выполняемая операция - это когда поток 2 переходит к int newCount = count + 1 и думает о том же, что и поток 1 («теперь счетчик равен 6»). Итак, два элемента только что были добавлены в очередь, но счетчик только увеличился с 5 до 6.

Это просто очень простой пример того, как небезопасный для потоков метод, такой как Queue .Enqueue может запутаться, когда доступ не синхронизирован. В нем конкретно не объясняется, что происходит в вашем вопросе; я просто хочу указать, что то, что вы делаете, не является потокобезопасным и вызовет неожиданное поведение .

8
ответ дан 6 December 2019 в 05:55
поделиться

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

8
ответ дан 6 December 2019 в 05:55
поделиться

Как указано в комментарии, ссылка _ на недоступна с геймпада, если не указан соответствующий вспомогательный модуль, но url _ для имеет значение. Поэтому я бы сделал почти то, что сказала Эмили, за исключением использования url _ для вместо жесткого кодирования URL.

например. если задание было определено как ресурс в ваших маршрутах:

link = "<a href=\"#{url_for(update)}\">#{update.id}</a>"    
flash[:notice] = "Created job number #{link}"
-121--1838576-

Почему бы просто не использовать GLIB?

Среди множества других вещей он имеет библиотечные функции для разбора INI-файлов, таких как файлы конфигурации:

https://developer.gnome.org/glib/stable/glib-Key-value-file-parser.html

Кроме того, что он также поддерживает типы данных (списки, хэштаблы, последовательности,

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

-121--2263933-

Guffa правильна, при использовании нескольких потоков для чтения и записи в очередь возникнут проблемы, поскольку очередь < T > не является безопасной для потока.

Если вы используете .NET 4, используйте класс ConcurrentQueue < T > , который безопасен для потоков. Если вы не находитесь на .NET 3 или ранее, вы можете либо выполнить собственную блокировку, как указал Гуффа, либо использовать библиотеку стороннего производителя.

5
ответ дан 6 December 2019 в 05:55
поделиться

Почему вы не решаете проблему таким образом?

while (IsRunning)
{
    do
    {
        MyObj myObj = queue.Dequeue();
        if (myObj != null)
        {
            DoSomethingWith(myObj);
        }
    } while (myObj != null);

    myAutoResetEvent.WaitOne();
}

Обновление

Хорошо, после прочтения комментария Эрвикерса и всех других ответов вы все в порядке, и я просто ложь. Поэтому , пожалуйста, не используйте приведенный выше код в многопоточном контексте .

0
ответ дан 6 December 2019 в 05:55
поделиться

Убедитесь, что ничего не помещает нулевые значения в очередь. null разрешены как значения в очереди. Кроме того, согласно этого документа , только статические элементы Queue являются потокобезопасными, поэтому будьте осторожны при чтении и записи между потоками.

2
ответ дан 6 December 2019 в 05:55
поделиться

Если вы используете неуниверсальную очередь (я не рекомендую ее использовать), вы можете использовать метод Queue.Synchronized для получения потока- безопасная оболочка:

Queue queue = Queue.Synchronized(new Queue());

В противном случае вам следует позаботиться о блокировке, как предлагают другие.

0
ответ дан 6 December 2019 в 05:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: