【问题标题】:Application Crashes on Thread Exit - C++线程退出时应用程序崩溃 - C++
【发布时间】:2011-05-17 23:22:55
【问题描述】:

我的应用程序在退出线程函数时崩溃。这就是我的线程的初始化方式:

LPTHREAD_START_ROUTINE pThreadStart = (LPTHREAD_START_ROUTINE)NotifyWindowThreadFn;
void * pvThreadData = reinterpret_cast<void *>(_pobjSerialPort);

// Create the exit notify window thread event handle.
_hNotifyWindowThreadExitEvent = ::CreateEvent(
    NULL,                           // No security
    TRUE,                           // Create a manual-reset event object
    FALSE,                          // Initial state is non-signaled
    NULL                            // No name specified
    );

if ( _hNotifyWindowThreadExitEvent == NULL )
{
    TRACE(_T("CreateNotifyWindow : Failed to get a handle for the exit message-only window event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
    return ::GetLastError();
}

// Create the notify window thread to begin execution on its own.
_hNotifyWindowThread = ::CreateThread(
    NULL,                           // No security attributes.
    0,                              // Use default initial stack size.
    pThreadStart,                   // Function to execute in new thread.
    pvThreadData,                   // Thread parameters.
    0,                              // Use default creation settings.
    NULL                            // Thread ID is not needed.
    );

if ( _hNotifyWindowThread == NULL )
{
    TRACE(_T("CreateNotifyWindow : Failed to create handle for message-only window thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
    return ::GetLastError();
}

这是我的线程函数中被执行的部分:

DWORD NotifyWindowThreadFn( void * pParam )
{
    static CNotifyWindow * pobjNotifyWindow = NULL;
    CSerialPort * pobjSerialPort = reinterpret_cast<CSerialPort *>(pParam);

    // Create notify window to handle surprize removal/insertion events...
    try
    {
        pobjNotifyWindow = new CNotifyWindow();
    }
    catch ( DWORD error )
    {
        return error;                // 1. PC gets here
    }
    catch ( long error )
    {
        return error;
    }
    catch ( ... )
    {
        return ERROR_CANNOT_MAKE;
    }

    /* Other stuff that is not executed due to return. */

}                                     // 2. PC then gets here

当应用程序崩溃时,Visual Studio 会给我这个错误消息:

Windows 已在 CppTestConsole.exe 中触发断点。

这可能是由于堆损坏,这表明 CppTestConsole.exe 或其已加载的任何 DLL 中存在错误。

这也可能是由于用户在 CppTestConsole.exe 获得焦点时按 F12。

输出窗口可能有更多的诊断信息。

输出窗口没有什么特别有用的东西。只有……

线程“NotifyWindowThreadFn”(0x414) 已退出,代码为 0 (0x0)。

然后它显示一堆DLL被卸载。当我单击 Break 按钮时,PC 位于 dbgheap.c 中 _CrtIsValidHeapPointer 的末尾。有没有人知道为什么我的应用程序在线程退出时崩溃?我不应该直接从线程函数中返回吗?谢谢。

【问题讨论】:

    标签: c++ multithreading visual-studio-2008 crash heap-memory


    【解决方案1】:

    你应该声明和定义你的函数为:DWORD WINAPI NotifyWindowThreadFn(void * pParam)

    【讨论】:

    • 对此+1。事实上,您根本不需要转换函数指针。你需要指出有问题。
    • 并移除那个可怕的、不必要的和危险的演员表
    • @Leo Davidson:不完全是。如果用 C 语言为 COM 编程,你就不能用它们(我想)。
    • @Jim Fell:如果您在 Windows 程序上使用了演员表,那么您也必须删除它,并使该函数使用 WINAPI。
    • 我的意思是如果你需要在那种情况下转换一个函数指针。我同意有时需要强制转换(例如 GetProcAddress 或您的 QueryInterface 示例)。
    【解决方案2】:

    尝试使用_beginthreadex 代替 CreateThread:

    可执行文件中调用的线程 C 运行时库 (CRT) 应该 使用 _beginthreadex 和 _endthreadex 函数用于线程管理而不是 CreateThread 和退出线程;这需要使用 的多线程版本的 阴极射线管。如果使用创建的线程 CreateThread 调用 CRT,CRT 可以终止进程 内存不足的情况。

    【讨论】:

    • @Jim Fell 似乎没有做任何会引起这个问题的事情。此外,在使用 CRT 的线程中使用 CreateThread() 引起的大多数(不是全部)问题早已得到修复。这可能不是问题。
    • 可能是对的,尽管创建一个新对象也不应该破坏堆/堆栈(即使它是一个窗口)。不过,我认为 Edwin 已经发现了问题所在。 (或者至少 一个 问题。可能是几个。:))
    【解决方案3】:

    我可能错了,但您似乎正在尝试从工作线程创建一个窗口。不要这样做。 Windows 需要消息泵才能运行,而您的应用程序中只有一个消息泵——它位于主线程中。

    【讨论】:

    • WndProc函数在收到消息时还会执行吗?我认为CreateWindow 不会在新线程中启动窗口;它在创建它的线程中执行。
    • 我只是嘲笑自己说“相信我”。如果有人对我这么说,我会说,“是的,对。”不管怎样,解释一下为什么你认为你希望你的窗口在工作线程中运行,我们可以讨论一下。
    • 基本上,创建窗口只是为了处理意外的 USB 移除和插入事件。它没有用户界面。引发这些事件时,WndProc 需要异步运行以尽快将 USB 设备的状态标记给应用程序的其余部分,以便可以根据需要挂起或恢复 USB 端口的串行 I/O,而影响最小(即丢失数据)到应用程序尽可能。
    • @Jim:WndProc 真的需要异步运行吗?还是当前在 WndProc 中的标记代码需要异步运行?如果是后者,我建议将您的窗口留在主线程中,当您的触发器触发时,启动一个工作线程或向一个休眠的工作线程发出信号以在那里进行标记。
    • 休眠的工作线程几乎不占用任何资源,而且工作线程休眠是非常常见和惯用的,它只等待这样一个信号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    相关资源
    最近更新 更多