MFC, Memory Leak при передаче параметров в поток

459
07 марта 2017, 16:58

День добрый. Уже полтора дня пытаюсь разобраться в причинах сообщений об утечках памяти(Memory Leaks Detected...) в OutPut'е проекта. И за эти полтора дня мне удалось выяснить, что утечки связаны с потоком и передачей ему параметров.

Программа выглядит примерно так: Диалоговое окно -> На нём размещён CTabCtrl -> Окно, которое активируется, когда открыта определённая вкладка.

В NewTabDlg.h объявлена структура и переменные:

HANDLE hListScan;
CWinThread* cwListScan;
CListCtrl ItemList;
CButton ScanEnabled;
typedef struct SData{
    CButton* Button;
    CListCtrl* List;
}SDataTrans;

В NewTabDlg.cpp, при изменении значения CheckBox'а создаётся поток:

void NewDialog::OnBnClickedCheckscan(){
    SDataTrans *data = new SDataTrans;
    data->Button = &ScanEnabled;
    data->List = &ItemList;
    if (ScanEnabled.GetCheck() > 0){
        cwListScan = AfxBeginThread(&ListScanThread, (LPVOID)data, NULL, NULL, NULL, NULL);
        if(cwListScan->m_hThread)
            hListScan = cwListScan->m_hThread;
    }
}

И сама функция потока:

UINT ListScanThread(LPVOID pParam) {
    SDataTrans * data = (SDataTrans *)pParam;
    while (data->Button->GetCheck() > 0) {
        CString strBuffer;
        for (int i(0); i <= MAX_ITEMS; i++) {
            if (lstrcmp(data->List->GetItemText(i, 0), SProd[i].Name) != 0)
                data->List->SetItemText(i, 0, SProd[i].Name);
            strBuffer.Format(_T("%i"), SProd[i].iPrice);
            if (lstrcmp(data->List->GetItemText(i, 1), strBuffer) != 0)
                data->List->SetItemText(i, 1, strBuffer);
            strBuffer.Format(_T("%i"), SProd[i].iAmount);
            if (lstrcmp(data->List->GetItemText(i, 2), strBuffer) != 0)
                data->List->SetItemText(i, 2, strBuffer);
        }
        Sleep(300);
    };
    data->List->DeleteAllItems();
    delete data;
    return 0;
}

Собственно, варианты развития событий.

  1. Во время работы программы, если совсем не трогаешь CheckBox, поток не запускается и, соответственно, при закрытии приложения нет утечек.
  2. Если запустить поток, активировав CheckBox и закрыть программу не сняв галочку с CheckBox'а, появляются сообщения об утечке памяти "{1025} client block at 0x01AE6328, subtype c0, 68 bytes long.", "atlTraceGeneral - a CWinThread object at $01AE6328, 68 bytes long". Но, если в OnClose() родительского окна прописать: NewTabDlg.ScanEnabled.SetCheck(0); WaitForSingleObject(NewTabDlg.hListScan, INFINITE); OnDestroy(); То поток, судя по нагрузке не работает(т.е. ничего не делает), но и программа зависает и находится в ожидании и не завершается.

  3. Во время работы программы, играемся с CheckBox'ом, включаем/выключаем и поток за потоком успешно завершают. Убираем галочку с CheckBox'а и закрываем программу. -> Получаем кучу сообщений об утечке памяти, и чем больше раз включали/выключали, тем больше этих сообщений.

Собственно, даже предположить не могу из-за чего это происходит. Уже много чего перепробовал.

Answer 1

Так а где у вас в коде уничтожается объект CWinThread, указуемый cwListScan? Этот объект создается динамически внутри AfxBeginThread

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
    int nPriority, UINT nStackSize, DWORD dwCreateFlags,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
  ...
  CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
  ...
  return pThread;
}

Он обычно освобождается внутри AfxEndThread, вызываемого в конце потока

void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete /* = TRUE */)
{
  AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
  CWinThread* pThread = pState->m_pCurrentWinThread;
  ...
  if (bDelete)
    pThread->Delete();
  ...
}

У вас же вызова AfxEndThread нигде не видно.

Если вы не хотите вызвать AfxEndThread, то делать delete cwListScan - это ваша обязанность.

Код, конечно, ужасен и в других отношениях, но тем не менее есть немалая вероятность, что ваши memory leaks - это в том числе именно оно.

Answer 2

Проблема решена. Данный тред - яркий пример человеческой невнимательности, а именно - моей.

В функции, из которой открывался поток, не удалялась структура. В общем, правильно это выглядит так:

void NewDialog::OnBnClickedCheckscan(){
    SDataTrans *data = new SDataTrans;
    data->Button = &ScanEnabled;
    data->List = &ItemList;
    if (ScanEnabled.GetCheck() > 0){
        cwListScan = AfxBeginThread(&ListScanThread, (LPVOID)data, NULL, NULL, NULL, NULL);
        if(cwListScan->m_hThread)
            hListScan = cwListScan->m_hThread;
    }
    else { delete data; } // удаляем data
}
READ ALSO
Пример lstm нейросети [требует правки]

Пример lstm нейросети [требует правки]

Есть у кого пример lstm сети? На гитхабе не нашел того, что подходит

481
Чем структура отличается от класса? [дубликат]

Чем структура отличается от класса? [дубликат]

На данный вопрос уже ответили:

325
Вывод элементов std::map в отладчике

Вывод элементов std::map в отладчике

Пользуюсь gdbНаблюдаемый субъект имеет имя m_loginIndexer

380
Граничные условия и вывод графика

Граничные условия и вывод графика

Продолжение этой темы Ошибки в коде при решении уравнения переноса

263