3

我在我的应用程序中使用 Windows ThreadPools,每次调用 CreateThreadPoolWork() 时都会遇到 136 字节的内存泄漏,如通过 UMDH 所见:

+ 1257728 ( 1286424 -  28696)   9459 allocs BackTraceB0035CC
+    9248 (   9459 -    211)    BackTraceB0035CC    allocations

    ntdll!RtlUlonglongByteSwap+B52
    ntdll!TpAllocWork+8D
    KERNEL32!CreateThreadpoolWork+25
    ... My Code ...

我正在使用 Cleanup Group,因此根据文档,我没有调用 CloseThreadPoolWork()。

我处理线程池的代码是:

typedef PTP_WORK ThreadHandle_t;
typedef PTP_WORK_CALLBACK THREAD_ENTRY_POINT_T;

static PTP_POOL pool = NULL;
static TP_CALLBACK_ENVIRON CallBackEnviron;
static PTP_CLEANUP_GROUP cleanupgroup = NULL;

int mtInitialize()
{
    InitializeThreadpoolEnvironment(&CallBackEnviron);

    pool = CreateThreadpool(NULL);

    if (NULL == pool)
    {
        return -1;
    }

    cleanupgroup = CreateThreadpoolCleanupGroup();

    if (NULL == cleanupgroup)
    {
        return -1;
    }

    SetThreadpoolCallbackPool(&CallBackEnviron, pool);

    SetThreadpoolCallbackCleanupGroup(&CallBackEnviron, cleanupgroup, NULL);

    return 0; // Success
}

void mtDestroy()
{
    CloseThreadpoolCleanupGroupMembers(cleanupgroup, FALSE, NULL);
    CloseThreadpoolCleanupGroup(cleanupgroup);
    DestroyThreadpoolEnvironment(&CallBackEnviron);
    CloseThreadpool(pool);
}

//Create thread
ThreadHandle_t mtRunThread(THREAD_ENTRY_POINT_T entry_point, void *thread_args)
{
    PTP_WORK work = NULL;

    work = CreateThreadpoolWork(entry_point, thread_args, &CallBackEnviron);

    if (NULL == work) {
        // CreateThreadpoolWork() failed.
        return 0;
    }

    SubmitThreadpoolWork(work);

    return work;
}

//Wait for a thread to finish
void mtWaitForThread(ThreadHandle_t thread)
{
    WaitForThreadpoolWorkCallbacks(thread, FALSE);
}

难道我做错了什么?任何想法为什么我会泄漏内存?

4

1 回答 1

0

鉴于您的评论,我猜您想通了,但问题是您只调用 CloseThreadpoolCleanupGroupMembers().mtDestroy()

CloseThreadpoolCleanupGroupMembers()如果您有一个持久线程池,除非您定期调用,否则不会释放内存。您的代码和评论建议您这样做,但如果没有负责创建和销毁线程池的代码,我无法确认这一点。

我对持久线程池的建议是只调用CloseThreadpoolWork()回调函数。如果您要创建和销毁线程池,Microsoft 的建议会更有效,但如果您在应用程序的生命周期内维护一个线程池,则CloseThreadpoolWork()比定期调用更简单、更容易。CloseThreadpoolCleanupGroupMembers()

顺便说一句,只要您告诉CloseThreadpoolCleanupGroupMembers()取消任何挂起的回调(传递fCancelPendingCallbacksas TRUE)以确保CloseThreadpoolWork()在任何已清理的工作项上被调用,就可以安全地执行这两项操作:

您只能通过关闭工作对象来撤销它的成员资格,这可以使用 CloseThreadpoolWork 函数单独完成。线程池知道工作对象是清理组的成员,并在关闭它之前撤销其成员资格。这可确保当清理组稍后尝试关闭其所有成员时应用程序不会崩溃。反之则不然:如果您首先指示清理组关闭其所有成员,然后对现在无效的工作对象调用 CloseThreadpoolWork,您的应用程序将崩溃。

带有 C++ 的 Windows - 线程池取消和清理

于 2016-06-17T02:40:51.843 回答