terminateThRead()在线程上使用CloseHandle(),仅使用堆栈普通变量(不使用planc)会泄漏内

TerminateThread() with CloseHandle() on thread which uses only stack plain variables (without alloc) leaks memory?

本文关键字:planc 变量 泄漏 线程 CloseHandle terminateThRead 堆栈      更新时间:2023-10-16

我创建了简单的测试,该测试创建了两个线程;第一个( workerThreadFun )执行无限循环,第二个循环( workereGuardThreadFun )终止了它。

要终止的线程似乎并不是确实没有分配分配(至少在 workerThreadFun 内部),并且仅使用普通C类型的堆栈变量,因此我希望堆栈将通过 terminateThreadRread <</em>()带有 closehandle ()。

由于某种原因,此测试会泄漏我的Win7上的内存。

不平衡的堆分配在哪里?

#include <windows.h>
#include <synchapi.h>
#include <assert.h>
#include <stdio.h>
#define STK_SIZE 4097
typedef enum
{
    GRD_IDLE,
    GRD_READY,
    GRD_TASKSTARTING,
    GRD_TASKWAITING
} GuardThreadState;
typedef struct
{
    HANDLE mHworkerThread;
    HANDLE mHworkerGroupThread;
    volatile int mIsWorkerStarted;
    GuardThreadState mGuardThreadState;
    CRITICAL_SECTION mLock;
    CONDITION_VARIABLE mThreadReadyCond;
    CONDITION_VARIABLE mStartTaskCond;
    CONDITION_VARIABLE mTaskFinishedCond;
} WorkerThreadHolder;
/*
typedef VOID(WINAPI *PRtlFreeUserThreadStack)(HANDLE hProcess, HANDLE hThread);
static PRtlFreeUserThreadStack RtlFreeUserThreadStack = NULL;
*/
DWORD WINAPI WorkerThreadFun(_In_ LPVOID p);
DWORD WINAPI WorkerGuardThreadFun(_In_ LPVOID p);
void Start(WorkerThreadHolder *workerThreadHolderPtr);
void ExecuteTask(WorkerThreadHolder *workerThreadHolderPtr);
/*----------------------------------------------------------------------------*/
DWORD WINAPI WorkerThreadFun(_In_ LPVOID p)
{
    /* use stack variables only in this thread in hope the stack will be deallocated by TerminateThread() */
    WorkerThreadHolder *workerThreadHolderPtr = (WorkerThreadHolder *)p;
    volatile int i;
    workerThreadHolderPtr->mIsWorkerStarted = 1;
    /*WakeAllConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);*/
    /* do nothing for infinite long time */
    for(i = 0;; ++i)
        i = i;
    /*WakeAllConditionVariable(&workerThreadHolderPtr->mTaskFinishedCond);*/
    return 0;
}
DWORD WINAPI WorkerGuardThreadFun(_In_ LPVOID p)
{
    const DWORD taskExecutionTimeoutInMillisec = 1;
    WorkerThreadHolder *workerThreadHolderPtr = (WorkerThreadHolder *)p;
    EnterCriticalSection(&workerThreadHolderPtr->mLock);
    workerThreadHolderPtr->mGuardThreadState = GRD_READY;
    WakeAllConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);
    for (;;)
    {
        for (;;)
        {
            SleepConditionVariableCS(
                &workerThreadHolderPtr->mStartTaskCond,
                &workerThreadHolderPtr->mLock,
                INFINITE);
            if (workerThreadHolderPtr->mGuardThreadState == GRD_TASKSTARTING)
                break;
        }
        workerThreadHolderPtr->mGuardThreadState = GRD_TASKWAITING;
        {
            BOOL isTaskFinishedOk = FALSE;
            for (;;)
            {
                isTaskFinishedOk =
                    SleepConditionVariableCS(
                        &workerThreadHolderPtr->mTaskFinishedCond,
                        &workerThreadHolderPtr->mLock,
                        taskExecutionTimeoutInMillisec);
                if (!isTaskFinishedOk)
                    break;
            }
            if (isTaskFinishedOk)
            {
                /* never happens in this test */
            }
            else
            {
                BOOL isClosed;
                TerminateThread(workerThreadHolderPtr->mHworkerThread, 0);
                /*if (RtlFreeUserThreadStack != NULL)
                    RtlFreeUserThreadStack(GetCurrentProcess(), workerThreadHolderPtr->mHworkerThread);*/
                isClosed = CloseHandle(workerThreadHolderPtr->mHworkerThread);
                workerThreadHolderPtr->mIsWorkerStarted = 0;
                workerThreadHolderPtr->mHworkerThread =
                    CreateThread(
                        NULL,
                        STK_SIZE,
                        WorkerThreadFun,
                        (PVOID)workerThreadHolderPtr,
                        STACK_SIZE_PARAM_IS_A_RESERVATION,
                        NULL);
            }
        }
        workerThreadHolderPtr->mGuardThreadState = GRD_READY;
        WakeAllConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);
    }
    return 0;
}
void Start(WorkerThreadHolder *workerThreadHolderPtr)
{
    workerThreadHolderPtr->mGuardThreadState = GRD_IDLE;
    workerThreadHolderPtr->mIsWorkerStarted = 0;
    InitializeConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);
    InitializeConditionVariable(&workerThreadHolderPtr->mStartTaskCond);
    InitializeConditionVariable(&workerThreadHolderPtr->mTaskFinishedCond);
    InitializeCriticalSection(&workerThreadHolderPtr->mLock);
    workerThreadHolderPtr->mHworkerThread =
        CreateThread(
            NULL,
            STK_SIZE,
            WorkerThreadFun,
            (LPVOID)workerThreadHolderPtr,
            STACK_SIZE_PARAM_IS_A_RESERVATION,
            NULL);
    workerThreadHolderPtr->mHworkerGroupThread =
        CreateThread(
            NULL,
            0,
            WorkerGuardThreadFun,
            (LPVOID)workerThreadHolderPtr,
            0,
            NULL);
}
void ExecuteTask(WorkerThreadHolder *workerThreadHolderPtr)
{
    assert(workerThreadHolderPtr->mHworkerThread != NULL);
    assert(workerThreadHolderPtr->mHworkerGroupThread != NULL);
    EnterCriticalSection(&workerThreadHolderPtr->mLock);
    for (;;)
    {
        if (workerThreadHolderPtr->mGuardThreadState == GRD_READY /* && workerThreadHolderPtr->mIsWorkerStarted != 0 */)
            break;
        SleepConditionVariableCS(
            &workerThreadHolderPtr->mThreadReadyCond,
            &workerThreadHolderPtr->mLock,
            INFINITE);
    }
    /* just poll */
    for (;;)
    {
        if (workerThreadHolderPtr->mIsWorkerStarted != 0)
            break;
    }
    workerThreadHolderPtr->mGuardThreadState = GRD_TASKSTARTING;
    WakeAllConditionVariable(&workerThreadHolderPtr->mStartTaskCond);
    LeaveCriticalSection(&workerThreadHolderPtr->mLock);
}
/*----------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
    int i;
    WorkerThreadHolder workerThreadHolder;
    /*
    HMODULE NTLibrary = GetModuleHandleW(L"ntdll.dll");
    RtlFreeUserThreadStack = (PRtlFreeUserThreadStack)GetProcAddress(NTLibrary, "RtlFreeUserThreadStack");
    */
    Start(&workerThreadHolder);
    for(i = 0;; ++i)
    {
        ExecuteTask(&workerThreadHolder);
        printf("%d Execution started...n", i);
        /*fflush(stdout);*/
    }
    return 0;
}

通过Visual Studio 2015,VC命令行进行了测试:/gs-/analyze-/w3/zc:wchar_t/zi/gm/od/od/fd"debug vc140.pdb"/zc:inline/fp:precise/d" win32"/d" win32"/d" _debug" _debug" _console" _console" _console" _console" _console" _console"/d" _unicode"/d" unicode"/errorReport:提示/wx-/zc:forscope/gd/oy/mdd/fa" debug "/nologo/fo/fo" debug" debug "/fp"debug consoLeApplication1.pch"

链接器:/OUT:" C: Users Cherney Documents Visual Studio 2015 Projects consoleapplication1 debug consoleapplication1.exe"/subtest/nxcompat/pdb:" c: users users cherney cherney cherney documents documents Visual Stutio Visual Visual Visual Visual Studio Projects Visual Visual Studio consoleapplication1 debug consoleapplication1.pdb"/dynamiCbase" kernel32.lib"" user32.lib" gdi32.lib" winspool.lib.lib" comdlg32.lib"" oleaut32.lib" uuid.lib" odbc32.lib" odbccp32.lib"/debug/machine:x86/cormemental/pgd:" c: users users cherney documents cherney documents visual Studio Visual Studio 2015 project consoleapplication1.pgd"/subsystem:console/supestuac:" level ='asinvoker'uiaccess'uiaccess ='false'"/manifestfile:"debug consoLeApplication1.exe.exe.intermediate.manifest.manifest.manifest"/errorReport"/errorReport"/errorReeport:stript/nologo/nologo/tlbid:1

'terminateThread'是'危险',实际上可能不会发生某些脱位(请参阅https://learn.microsoft.com/en-en-us/windows/desktop/api/ProcessThreadSAPI/NF-PROCESSTHREADSAPI-terminateThread)。最好在不使用" terminateThread"的情况下重新设计代码以干净退出线程。