【发布时间】:2013-04-05 00:37:51
【问题描述】:
首先要说的是:我用一种相对陌生的语言编程:Blitzmax,这是一种面向对象的基本方言。
我的问题如下: 我写了一个在自己的线程中运行的调试管理器。因此,从程序中的每个位置(这将是一场游戏),您都可以将调试或错误消息添加到经理的队列中。 在它自己的线程中,它将从队列中获取消息并通过将它们写入文件来处理它们,并且(如果消息具有当前选择的 Debuglevel、Debugcategory 和 outputcategory,它们只是枚举)将其写入控制台。
现在我在三个系统上测试了这个程序:我的台式电脑,操作系统是 Windows 8,我自己的笔记本电脑是 Windows 7,朋友的笔记本电脑也是 Windows 7。 在我的电脑和我朋友的笔记本电脑上一切都很好。 但是在我自己的笔记本电脑上,几乎每次都会在经理处理消息时出现“EXCEPTION_ACCESS_VIOLATION”错误。 有时程序运行良好,但大多数时候它会因这个错误而崩溃。 即使在调试模式下也不会显示任何行或堆栈跟踪,这使得调试非常困难。
我将所有需要的类分解为最少的属性和功能,以便更容易找到问题。 现在队列只是一个列表(在 Blitzmax 中内置),消息只有一个属性,它是一个字符串。 此外,调试管理器仅将消息写入控制台,而不将其传递给将其写入文件等的进程方法。
所以实际需要的代码如下。
这是消息:
Type TThreadsafeMessage
Field complete_String:String
Method New_ThreadsafeMessage:TThreadsafeMessage(actual_Message:String, from_File:String, debugCategory:TDebugCategory_Enum, ..
debugLevel:TDebugLevel_Enum, outputCategory:TOutputCategory_Enum, from_Class:String = "", from_Method:String = "")
'Just create the string from the parameters.
Self.complete_String = actual_Message + " | " + from_File + "/" + from_Class + "/" + from_Method
Return Self
End Method
Method ToString:String()
'Just return the string attribute:
Return Self.complete_String' out_String
End Method
Method toString_Formatted_For_File:String()
Return Self.ToString()
End Method
Method toString_Formatted_For_Console:String()
Return Self.ToString()
End Method
End Type
这是队列:
Type TThreadsafeQueue
'Predefined list.
Field list:TList
Method New()
Self.list = New TList
End Method
Method isEmpty:Byte()
Return Self.list.IsEmpty()
End Method
Method enqueue(to_Enqueue:Object)
'Add object to list
Self.list.AddLast(to_Enqueue)
End Method
Method dequeue:Object()
Return Self.list.RemoveFirst()
End Method
End Type
这是向调试管理器添加消息的方法:
Function enqueueMessage(message_To_Enqueue:TThreadsafeMessage)
'Test message for null pointer.
If(message_To_Enqueue = Null) Then
Throw New TNullpointer_Exception.NewException("'message_To_Enqueue' is NULL.", "TDebugmanager.bmx", ..
"TDebugmanager", "enqueueMessage")
EndIf
'Lock mutex for threadsafety.
LockMutex(TDebugmanager.getSingleton_Instance().queue_Mutex)
'Enqeue message in the queue
TDebugmanager.getSingleton_Instance().message_Queue.enqueue(message_To_Enqueue)
'Tell the update thread there is a message
SignalCondVar(TDebugmanager.getSingleton_Instance().sleep_ConditionVariable)
'Free the mutex for update thread.
UnlockMutex(TDebugmanager.getSingleton_Instance().queue_Mutex)
End Function
现在这里是调试管理器的(当前较小的)更新功能:
Function _update:Object(thread_Object:Object)
'Do this over and over till the queue is empty AND the debugmanager is shut down
Repeat
Local message_To_Process:TThreadsafeMessage = Null
'Lock mutex for thread safety
LockMutex(TDebugmanager.getSingleton_Instance().queue_Mutex)
'Queue epmty...
If(TDebugmanager.getSingleton_Instance().message_Queue.isEmpty()) Then
'... Wait for a signal from the main thread
WaitCondVar(TDebugmanager.getSingleton_Instance().sleep_ConditionVariable, ..
TDebugmanager.getSingleton_Instance().queue_Mutex)
Else
'...Get the next message from the queue.
message_To_Process = TThreadsafeMessage(TDebugmanager.getSingleton_Instance().message_Queue.dequeue())
EndIf
'Unlock the mutex.
UnlockMutex(TDebugmanager.getSingleton_Instance().queue_Mutex)
'Check if the message is NULL.
If(message_To_Process = Null) Then
Throw "Got null message from queue."
EndIf
'Actually the _processMessage method is used. But for debugging
'it is commented out.
' TDebugmanager.getSingleton_Instance()._processMessage(message_To_Process)
'Write the message to the console.
'HERE is the error.
'See in the following description under the code section.
DebugLog("Message processed: " + message_To_Process.complete_String)
Until TDebugmanager.isFinished()
End Function
所以在它在更新函数中说“这里是错误”的位置。问题如下:如果此行被注释掉或删除,程序在我的笔记本电脑上运行良好,没有错误发生。 但是如果有这条线,那么大多数时候都会发生错误。 例如,在以下情况下会抛出“EXCEPTION_ACCESS_VIOLATION”:堆栈溢出发生在某处。或者当您尝试访问 NULL 对象时。 实际上所有试图从被禁止的内存中读取或写入的东西。 真正奇怪的是:仅在前几行,我检查了我从队列中得到的消息是否为 NULL。 如您所见,它应该会引发错误。 但它永远不会。
以前有人见过这样的行为吗? 我无法解释。 正如我所说:在这种情况下调试真的很难。我可以把它分解成更小的类,最后是你在这里看到的代码。 我也不能只用调试器一步一步地通过程序,因为这样就不会发生错误。 有人可能会想到什么会导致此时出现错误吗?
我知道,这是很多代码,但我不能让它更短。
【问题讨论】:
-
如果您真的怀疑堆栈大小溢出问题,请尝试增加程序的堆栈限制,然后验证您是否仍然在同一行遇到问题.../
-
目前我只用测试程序测试这个类。在这个测试程序中,一定数量的消息被添加到队列中并被处理。它也仅因 10 条消息而崩溃。 RAM 使用量没有显着增加。我只想展示有多少东西会导致这种类型的错误。我不相信这是堆栈大小,因为我用这种语言编写了更大的程序,而没有改变堆栈大小或 RAM 使用情况。
标签: multithreading thread-safety nullreferenceexception basic blitzmax