繁体   English   中英

TerminateThread() 和 CloseHandle() 在仅使用堆栈普通变量(无分配)的线程上泄漏内存?

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

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

要终止的线程似乎没有进行显式分配(至少在WorkerThreadFun内部)并且仅使用普通 C 类型的堆栈变量,所以我希望堆栈将被TerminateThread () 和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 /Fd"Debug\\vc140.pdb" /Zc:inline /fp:precise /D "WIN32" /D "_D​​EBUG" /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" 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

“ TerminateThread”是“危险的”,实际上某些取消分配可能不会发生(请参阅https://docs.microsoft.com/zh-cn/windows/desktop/api/processthreadsapi/nf-processthreadsapi-terminatethread )。 最好重新设计代码,以在不使用“ TerminateThread”的情况下干净地退出线程。

除了调试或某些非常致命的失败之外,根本不应该使用TerminateThread

因此,释放内存并不麻烦,因为很难实现这些重新分配,而且由于用户的分配,内存可能会泄漏。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM