我有一個令人難以置信的奇怪的NullReferenceException在從我知道存在的對象上的公共字段讀取值時拋出。基本流程是這樣的:奇怪的線程NullReferenceException在讀取存在的值時?
編輯:我意識到我忘了提一些重要的東西,這不會發生每次我試圖讀取Tag
值時間,但只有somtimes,以至於我可以重現它的每時間剛剛運行的代碼,但不立即當代碼運行
- 服務器接收郵件(工作線程)
- 發送消息get獲取上設置爲
Tag
場的連接消息對象(工作線程) - 的消息被放入一個「ReceivedMessages」隊列(工作線程)(其由用於串行化訪問鎖把守正常Queue對象)
- 的消息被讀(主線程)
- 我嘗試閱讀郵件的
Tag
領域獲得的連接,這有時會返回null,拋出一個異常,但是當異常被拋出,我檢查Message
對象我可以看到Connection
對象(這是Tag
字段中的對象)清除了一個節(主線程)
如果你看一下這張照片,你會看到它一清二楚:
你可以看到我的標有綠色方塊,我嘗試以三種不同的方式閱讀message.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;
,但這似乎沒有幫助。
我不是一個學者,但我認爲你正在混合鎖與易失性讀寫..我認爲這是問題所在。如果你使用volatile,你最好一直使用它..如果你正在使用鎖,那麼只鎖.. – 2011-11-27 10:47:15
嗯,不知道我是否同意 - 我鎖定所有線程都會見,volatile部分似乎不起作用它根本(如果我刪除它或添加它,沒有任何反應)。 – thr
是否有另一個線程對'message.Tag'字段進行操作(例如將其設置爲空)? – Hans