У меня невероятно странное исключение NullReferenceException, возникающее при чтении значения из публичного поля объекта, о существовании которого я знаю. Основной поток таков:
Edit: Я понял, что забыл упомянуть кое-что важное, это происходит не каждый раз, когда я пытаюсь прочитать значение Tag
, а только иногда, достаточно, чтобы я мог воспроизвести это каждый раз, просто запустив код, но не мгновенно, когда код выполняется
Tag
поля объекта сообщения (рабочий поток)Tag
сообщения, чтобы получить соединение, это иногда возвращает null и выбрасывает исключение, но когда исключение выбрасывается и я проверяю объект Message
, я вижу объект Connection
(который является объектом, находящимся в поле Tag
) там ясно как день (Main Thread)Если вы посмотрите на эту картинку, вы увидите это ясно как день:
Вы можете видеть, где я отметил зеленым квадратиком, я пытаюсь прочитать сообщение. Tag
свойство тремя различными способами, все они возвращают null, как вы можете видеть в части, отмеченной синей рамкой.
Однако, если вы посмотрите на две области, отмеченные красным цветом, вы увидите, что объект действительно существует. И, чтобы устранить путаницу, часть, где сообщение помещается в очередь полученных сообщений, выглядит так:
Как видите, я даже попробовал выполнить 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
на нулевое значение и добавляет его id в список под названием "isNullLog", если это так. Когда в главном потоке возникает NullReferenceException, этот список пуст.
Вы также видите, что я блокирую очередь peer.ReceivedMessages
и отправляю сообщение в очередь после установки поля message.Tag
.
Также, чтобы было еще более понятно, вот функция, которая используется для чтения сообщения из очереди 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;
но это, похоже, не помогает.