У меня возникает невероятно странное исключение NullReferenceException при чтении значения из открытого поля для объекта, который, как я знаю, существует.Основной поток таков:
Редактировать: Я понял, что забыл упомянуть кое-что важное, это происходит не каждый раз, когда я пытаюсь прочитать значение Tag
, но только иногда , достаточно, чтобы я мог воспроизвести его каждый раз, просто запустив код, но не сразу, когда код запускается
- Сервер получает сообщение (рабочий поток)
- Соединение, отправившее сообщение, получает в качестве поля
Tag
объекта сообщения (рабочий поток) - Сообщение помещается в очередь «ReceivedMessages»(обычный объект Queue, который защищен блокировками для сериализованного доступа) (рабочий поток)
- Сообщение читается (основной поток)
- Я пытаюсь прочитать поле
Tag
сообщения, чтобы получить соединение, иногда оно возвращает ноль и выдает исключение, но когда возникает исключение, и я проверяю объект Message
, я вижу объект Connection
(которыйэто объект, который находится в Tag
поле) там ясно как день (основная тема)
Если вы посмотрите на это изображение, вы увидите, что оно ясно как день:
Вы можете видеть, где я пометил зеленую рамку, я пытаюсь прочитать свойство 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;
, но это, похоже, не помогает.