在线程上使用CloseHandle()的TerminateThread()只使用堆栈普通变量(没有alloc)泄漏内存?

问题描述 投票:0回答:2

我创建了简单的测试,创建了两个线程;第一个(WorkerThreadFun)执行无限循环,第二个(WorkerGuardThreadFun)以小超时终止它。

要终止的线程似乎没有进行expicit分配(至少在WorkerThreadFun中)并且只使用普通C类型的堆栈变量,所以我希望使用CloseHandle()通过TerminateThread()释放堆栈。

由于某种原因,这个测试泄露了我的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 /Fd"Debug\vc140.pdb“/ Zc:inline / fp:precise / D”WIN32“ / D“_DEBUG”/ D“_CONSOLE”/ D“_UNICODE”/ D“UNICODE”/ errorReport:prompt / WX- / Zc:forScope / Gd / Oy / MDd / Fa“Debug \”/ nologo / Fo“Debug \ “/Fp"Debug\ConsoleApplication1.pch”

链接器:/ OUT:“C:\ Users \ cherney \ documents \ visual studio 2015 \ Projects \ ConsoleApplication1 \ Debug \ ConsoleApplication1.exe”/ MANIFEST / NXCOMPAT / PDB:“C:\ Users \ cherney \ documents \ visual studio 2015 \ Projects \ ConsoleApplication1 \ Debug \ ConsoleApplication1.pdb“/ DYNAMICBASE”kernel32.lib“”user32.lib“”gdi32.lib“”winspool.lib“”comdlg32.lib“”advapi32.lib“”shell32.lib“”ole32。 lib“”oleaut32.lib“”uuid.lib“”odbc32.lib“”odbccp32.lib“/ DEBUG / MACHINE:X86 / INCREMENTAL / PGD:”C:\ Users \ cherney \ documents \ visual studio 2015 \ Projects \ ConsoleApplication1 \ Debug \ ConsoleApplication1.pgd“/ SUBSYSTEM:CONSOLE / MANIFESTUAC:”level ='asInvoker'uiAccess ='false'“/ ManifestFile:”Debug\ConsoleApplication1.exe.intermediate.manifest“/ ERRORREPORT:PROMPT / NOLOGO / TLBID:1

c++ c winapi
2个回答
4
投票

'TerminateThread'是'危险的',事实上可能不会发生一些解除分配(参见https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-terminatethread)。最好重新设计代码,以便在不使用“TerminateThread”的情况下干净地退出线程。


0
投票

除了调试或一些非常致命的故障之外,TerminateThread根本不应该被使用。

因此,它不需要释放内存,因为这些解除分配很难实现,并且无论如何内存可能由于用户的分配而泄漏。

因此,即使您创建了挂起的线程,也会有一些内存泄露,并且在终止之前永远不会让它工作!

这个泄露内存的最大部分是堆栈本身,可能不是很大的提交内存,而是整个兆字节的虚拟地址空间(如果我们假设默认堆栈)。

© www.soinside.com 2019 - 2024. All rights reserved.