Странный поток NullReferenceException при чтении значения, которое существует? - PullRequest
6 голосов
/ 27 ноября 2011

У меня возникает невероятно странное исключение NullReferenceException при чтении значения из открытого поля для объекта, который, как я знаю, существует.Основной поток таков:

Редактировать: Я понял, что забыл упомянуть кое-что важное, это происходит не каждый раз, когда я пытаюсь прочитать значение Tag, но только иногда , достаточно, чтобы я мог воспроизвести его каждый раз, просто запустив код, но не сразу, когда код запускается

  • Сервер получает сообщение (рабочий поток)
  • Соединение, отправившее сообщение, получает в качестве поля Tag объекта сообщения (рабочий поток)
  • Сообщение помещается в очередь «ReceivedMessages»(обычный объект Queue, который защищен блокировками для сериализованного доступа) (рабочий поток)
  • Сообщение читается (основной поток)
  • Я пытаюсь прочитать поле Tag сообщения, чтобы получить соединение, иногда оно возвращает ноль и выдает исключение, но когда возникает исключение, и я проверяю объект Message, я вижу объект Connection (которыйэто объект, который находится в Tag поле) там ясно как день (основная тема)

Если вы посмотрите на это изображение, вы увидите, что оно ясно как день:

Weird thread behavior

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

Однако, если вы посмотрите на две области, отмеченные красным, вы можете ясно видеть, как день, что объект действительно существует.И, просто чтобы устранить любую путаницу, часть, где сообщение помещается в очередь полученных сообщений, выглядит следующим образом:

Я, как вы можете видеть, я даже пытался сделать Thread.VolatileWrite, чтобы убедиться, чтозаписывается значение

message.Tag = buffer.Tag;

Thread.VolatileWrite(ref message.Tag, buffer.Tag);

if (message.Tag == null)
{
    isNullLog.Add(message.Id);
}

// Queue into received messages
lock (peer.ReceivedMessages)
{
    peer.ReceivedMessages.Enqueue(message);
}

Все приведенные выше фрагменты выполняются в рабочем потоке, и, как вы можете видеть, я копирую buffer.Tag в message.Tag, я даже настроил немного времени выполненияпроверка на отладку, которая проверяет message.Tag на нулевое значение и добавляет его идентификатор в список с именем isNullLog, если это так.Когда исключение NullReferenceException генерируется в основном потоке, этот список становится пустым.

Вы также видите, что я блокирую очередь peer.ReceivedMessages и отправляю сообщение в очередь после того, как я установилmessage.Tag field.

Кроме того, чтобы быть еще более понятным, здесь представлена ​​функция, используемая для чтения сообщения из очереди peer.ReceivedMessages:

public bool TryGetMessage(out TIncomingMessage message)
{
    lock (ReceivedMessages)
    {
        if (ReceivedMessages.Count > 0)
        {
            message = ReceivedMessages.Dequeue();
            return true;
        }
    }

    ReceivedMessageEvent.Reset();

    message = null;
    return false;
}

Вы можете видеть, чтоЯ блокирую очередь еще до того, как проверил счетчик, и если он не пуст, я устанавливаю свойство out и возвращаю true, в противном случае я возвращаю false.

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

Небольшое обновление, я также пытался пометить поле Tag как volatile, чтобы оно выглядело так public volatile object Tag;, но это, похоже, не помогает.

1 Ответ

2 голосов
/ 27 ноября 2011

Я действительно исправил это прямо сейчас, как всегда, когда имеешь дело с потоками, вам нужно быть очень осторожным при чтении / записи значений. Я забыл очистить локальную переменную message в цикле приема и закончил тем, что "повторно использовал" то же самое сообщение в следующей итерации цикла, поскольку у него есть проверка if(message == null) { /* create new message */ } перед каждой итерацией и когда я не очищал это чтение В итоге поток попрал «старое» сообщение, которое хранилось здесь, когда пытался написать в него новое сообщение!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...