Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 读取存在的值时出现奇怪的线程NullReferenceException?_C#_.net_Windows_Multithreading_Thread Safety - Fatal编程技术网

C# 读取存在的值时出现奇怪的线程NullReferenceException?

C# 读取存在的值时出现奇怪的线程NullReferenceException?,c#,.net,windows,multithreading,thread-safety,C#,.net,Windows,Multithreading,Thread Safety,当我从一个我知道存在的对象的公共字段中读取一个值时,抛出了一个不可思议的NullReferenceException异常。基本流程如下: Edit:我意识到我忘了提到一些重要的事情,这并不是每次我尝试读取标记时都会发生的,但只有在某些时候,这足以让我每次只运行代码就可以重现它,而不是在代码运行时立即重现 服务器收到消息(工作线程) 发送消息get的连接被设置为消息对象(工作线程)上的标记字段 消息被放入一个“ReceivedMessages”队列(一个普通队列对象,由用于序列化访问的锁保护)(

当我从一个我知道存在的对象的公共字段中读取一个值时,抛出了一个不可思议的NullReferenceException异常。基本流程如下:

Edit:我意识到我忘了提到一些重要的事情,这并不是每次我尝试读取
标记时都会发生的,但只有在某些时候,这足以让我每次只运行代码就可以重现它,而不是在代码运行时立即重现

  • 服务器收到消息(工作线程)
  • 发送消息get的连接被设置为消息对象(工作线程)上的
    标记
    字段
  • 消息被放入一个“ReceivedMessages”队列(一个普通队列对象,由用于序列化访问的锁保护)(工作线程)
  • 消息被读取(主线程)
  • 我尝试读取消息的
    标记
    字段以获取连接,这有时返回null并引发异常,但当引发异常并检查
    消息
    对象时,我可以看到
    连接
    对象(即
    标记
    字段中的对象)在那里清晰可见(主线程)
如果你看这张图片,你会清楚地看到:

您可以看到我用绿色框标记的地方,我尝试用三种不同的方式读取
message.Tag
属性,它们都返回null,正如您在用蓝色框标记的部分中看到的那样

但是,如果您查看标记为红色的两个区域,您可以清楚地看到对象实际存在的日期。并且,为了消除任何混淆,将消息放入接收消息队列的部分如下所示:

正如您所看到的,我甚至尝试执行Thread.VolatileWrite以确保写入值

上面的代码段都发生在工作线程中,正如您所看到的,我将
缓冲区.Tag
复制到
消息.Tag
,我甚至设置了一个小的运行时调试检查,检查
消息.Tag
是否有空值,并将其id添加到名为“isNullLog”的列表中如果是这样,当主线程中抛出NullReferenceException时,此列表为空

您还可以看到,在设置了
message.Tag
字段后,我锁定了
peer.ReceivedMessages
队列,并将消息推送到队列中

此外,更清楚的是,这里有一个函数,用于从
对等方.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

老实说,我完全被难倒了,以前写过几个多线程应用程序,从来没有遇到过这种情况


有一点更新,我还尝试将
标记
字段标记为
volatile
,使其看起来像
public volatile object标记;
,但这似乎没有帮助。

我现在确实解决了这个问题,就像处理线程时一样,在读取/写入值时需要非常小心。我忘记了ing清除接收循环中的本地
消息
变量,并在下一个循环迭代中“重用”同一消息,因为它在每次迭代之前都有一个
if(message==null){/*create new message*/}
检查,而当我没有清除此项时,读取线程最终会践踏“旧”消息尝试向其中写入新消息时存储在此处的消息!

我不是学者,但我认为您将锁与volatile读写混合在一起..我认为这就是问题所在。如果您使用volatile,您最好一直使用它..如果您使用锁,则只使用锁..嗯,不确定我是否同意-我在所有线程相遇的地方都有锁,易失性部分似乎根本不会影响它(如果我删除或添加它,则不会发生任何情况)。是否有另一个线程在
message.Tag
字段上运行(例如将其设置为null)你说得对,我刚刚注意到第一行:message.Tag=buffer.Tag;有,但只有当消息被推回到“used messages”队列时,它才会对其进行操作,直到我调用“TryRecycleMessage(out tincoming message)”时才会发生这种情况。如果另一个线程正在将其置零,为什么在引发异常时它会在检查器中显示一个值?您可能有太多的检查:)不要麻烦检查或清除message*对象实例变量-只要养成在开始时创建新实例的习惯,以及在将一个实例排队后的下一步。
public bool TryGetMessage(out TIncomingMessage message)
{
    lock (ReceivedMessages)
    {
        if (ReceivedMessages.Count > 0)
        {
            message = ReceivedMessages.Dequeue();
            return true;
        }
    }

    ReceivedMessageEvent.Reset();

    message = null;
    return false;
}