typedef
:
typedef struct tagMYSTRUCT {
wchar_t mystr[40] = { 0 };
DWORD threadId = NULL;
HANDLE threadHandle = NULL;
HWND receiverWnd = NULL;
} MYSTRUCT, *PMYSTRUCT;
线程创建:
MYSTRUCT ats = *(PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT));
wcscpy(ats.mystr, L"hello");
ats.threadHandle = CreateThread(NULL, 0, MyThread, &ats, 0, &(ats.threadId));
这是使用HeapFree()函数的线程。但它崩溃了。我相信这是不好的做法,但我想知道为什么。什么是背后的逻辑以及为什么HeapFree崩溃程序?
DWORD WINAPI MyThread(LPVOID lpParam) {
MYSTRUCT ActiveStruct = *(PMYSTRUCT)lpParam;
if (lpParam != NULL) {
std::cout << "1" << std::endl; // Gets printed.
HeapFree(GetProcessHeap(), NULL, lpParam);
std::cout << "2" << std::endl; // Crashes before this line.
}
...
}
答案 0 :(得分:3)
你显然来自另一种语言,它将指针和引用的概念与C ++区别开来。您在C ++中的使用非常不合适。通过使用非标准函数(HeapAlloc()
是特定于Windows的,而不是C ++等)来管理内存,你会进一步复杂化。
如果您要使用HeapAlloc()
(非标准C ++,特定于Windows)或任何动态分配内存的标准函数,结果需要存储在指针中。
MYSTRUCT ats = *(PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT));
wcscpy(ats.mystr, L"hello");
ats.threadHandle = CreateThread(NULL, 0, MyThread, &ats, 0, &(ats.threadId));
这样做是将HeapAlloc()
返回的指针转换为指向MYSTRUCT
的指针,取消引用将该内存位置解释为MYSTRUCT
的值的指针,并复制该值进入ats
。
至少这是一个内存泄漏 - 由HeapAlloc()
分配的内存丢失(从未再次使用,它的地址没有存储在任何地方),并且您将ats
的地址传递给线程功能
因此HeapAlloc()
分配的内存与传递给线程函数的地址之间没有任何关系。
更糟糕的是线程函数本身,我在这里简化了
DWORD WINAPI MyThread(LPVOID lpParam)
{
MYSTRUCT ActiveStruct = *(PMYSTRUCT)lpParam;
if (lpParam != NULL)
{
std::cout << "1" << std::endl; // Gets printed.
HeapFree(GetProcessHeap(), NULL, lpParam);
std::cout << "2" << std::endl; // Crashes before this line.
}
}
lpParam
将包含创建该线程的函数传递的函数中ats
的地址。
如果创建线程的函数已经返回(毕竟,线程并行运行),那么ats
将不再存在。如果发生这种情况,lpParam
将是一个悬空指针(就您的程序而言,对象的地址不再存在)。
ActiveStruct
现在将成为一个本地对象,其中包含传递给函数的地址处的对象副本。换句话说,它是由func先前分配的ats
的本地副本。如果ats
已不复存在,并且传递的地址悬空,则创建ActiveStruct
的简单行为会导致未定义的行为。
更糟糕的是,lpParam
是({1}}的地址。如果ats
仍然存在(即创建线程的函数未返回),则不会在堆上创建它,因此不应使用ats
释放。如果它不再存在,则不应将其传递给HeapFree()
。无论哪种方式,都要求HeapFree()
释放未使用HeapFree()
分配的内存。这几乎可以保证导致运行时错误。
至少,您需要将创建线程的代码更改为
HeapAlloc()
和线程函数
MYSTRUCT *ats = (PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT)); // note changed position of *
wcscpy(ats->mystr, L"hello"); // note usage of ats as a pointer
DWORD threadID; // we need these since ats is being released by the thread function
HANDLE threadHandle; // it is not a good idea for CreateThread() to use them
threadHandle = CreateThread(NULL, 0, MyThread, ats, 0, &(threadId)); // changes since ats is now a pointer
由于您对C ++内存模型做出了根本错误的假设,我认为代码中的其他内容(您没有显示)是错误的。但这应该让你开始。
答案 1 :(得分:1)
你对此感到困惑。您正在传递堆栈分配结构的地址,而您不打算这样做。我认为你打算将地址传递给堆分配的结构。当您尝试释放该结构时,调用HeapFree
会遇到运行时错误,因为您将HeapFree
未分配的内存地址传递给HeapAlloc
。
我将使用new
和delete
而非HeapAlloc
和HeapFree
向您展示如何完成此操作。这里真的没有必要使用HeapAlloc
。使用标准C ++内存分配器。
MYSTRUCT *pats = new MYSTRUCT(); // zero initialise
wcscpy(pats->mystr, L"hello");
DWORD threadId;
HANDLE threadHandle = CreateThread(NULL, 0, MyThread, pats, 0, &threadId);
....
DWORD WINAPI MyThread(LPVOID lpParam)
{
MYSTRUCT ActiveStruct = *(PMYSTRUCT)lpParam;
delete (PMYSTRUCT)lpParam;
// if you want the thread ID, call GetCurrentThreadId
// or if you want a thread handle call GetCurrentThread
}
请注意,我没有尝试将线程句柄和线程ID直接存储到结构中。这是因为理论上可以在调用CreateThread
之前销毁该结构。我改用了局部变量。如果您的线程需要找到它的ID,或者获取自己的句柄,那么就会有API调用来执行此操作。
答案 2 :(得分:0)
1)你没有在转换和解除引用之前检查指针
2)您实际上是在堆栈上分配MYSTRUCT并从MYSTRUCT大小的堆分配缓冲区中复制零
3)堆分配的指针在复制分配后泄漏
4)你将指向堆栈分配的MYSTRUCT实例的指针传递给CreateThread,它在MYSTRUCT超出范围之后变得无效(这可能发生在新线程启动之前的任何时候,当它运行时,或者在它退出之后)
5)CreateThread和c ++运行时不能很好地协同工作
答案 3 :(得分:0)
如果您坚持使用HeapAlloc
,这是更正后的版本PMYSTRUCT ats = (PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT));
if(ats == NULL)
return; // exit, throw do something
wcscpy(ats->mystr, L"hello");
CreateThread(NULL, 0, MyThread, ats, 0, &(ats.threadId));
HeapAlloc返回指向已分配内存的指针,该指针放在指针中。然后使用指针操作分配的结构,最后将此指针传递给线程。并且不要将createthread的结果分配给线程所破坏的东西。