0

首先要说的是:我使用一种相对未知的语言进行编程: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。如您所见,它应该会引发错误。但它永远不会。

有没有人见过这样的行为?我无法解释。正如我所说:在这种情况下调试真的很难。我可以把它分解成更小的类,最后是你在这里看到的代码。我也不能只用调试器一步一步地通过程序,因为这样就不会发生错误。有人可能会想到会导致这一刻错误的事情吗?

我知道,这是很多代码,但我不能让它更短。

4

2 回答 2

0

您基本上是在同时写入流。如果DebugLog不是可重入的(例如,内部使用了一些静态缓冲区),您将获得随机访问冲突。在更快的机器上,这也许可以避免,因为消息队列会发生争用,因此每个线程都有时间自己完成一个完整的循环。

解决方案是锁定该函数的使用(您可能希望制作一个函数包装器,它独立于队列处理实现该锁定)。

于 2013-04-12T21:22:45.553 回答
0

问题如下:当我尝试进一步调试程序时,有时调试器会启动并向我显示调试器认为错误发生的位置。它没有在那里发生,但我能够使用调试器的 step-In 功能。结果是:它跳进了一个用于网络方法的库。我不在项目的任何地方使用这个库。所以我进一步尝试了一点,实际的解决方案是:卸载 Blitzmax 并重新安装它。现在一切正常。似乎链接器以某种方式被破坏了。

于 2013-04-14T13:13:04.863 回答