代码之家  ›  专栏  ›  技术社区  ›  John Mansell

无法将CDocument保存在工作线程中--在线程启动之前,对象已从内存中销毁

  •  0
  • John Mansell  · 技术社区  · 4 年前

    概述

    我需要在后台工作线程中保存一个CDocument。在我们的MFC应用程序中有一点提示用户在继续之前进行保存。通常情况下,他们可以在不储蓄的情况下继续,这是没有问题的。然而,偶尔我们会在稍后的过程中需要该文档,因此如果用户单击“否”,我们希望在后台保存该文件的临时版本,而不让用户等待保存继续。

    问题

    当我启动时 AfxBeginThread(SaveDocumentThread, &threadInput) &在SaveDocumentThread启动之前,线程输入已从内存中清除。

    密码

    BOOL SPackagerDoc::OnSaveDocument( IN LPCTSTR lpszPathName)
    {
        ProcessDocumentThreadInput threadInput(this, lpszPathName);
        
        // Temp Save Mode
        if (m_bTempMode)
        {
            m_TempSaveThread = AfxBeginThread(SaveDocumentThread, &threadInput);
            // This fixes the problem, but is considered unstable
            // if (m_TempSaveThread->m_hThread)
            //        WaitForSingleObject(m_TempSaveThread->m_hThread, 500);
            return TRUE;
        }
    
        // Normal save mode
        SFileLoadingDialog loadingDialog(SFileLoadingDialog::SAVE, lpszPathName, SaveDocumentThread, &threadInput);
        BOOL result = (BOOL)loadingDialog.DoModal();
    
        return result;
    } 
    
    StUInt32 SPackagerDoc::SaveDocumentThread(IN StVoid* pParam)
    {
        ProcessDocumentThreadInput* input = (ProcessDocumentThreadInput*)pParam;
        ASSERT_NOT_NULL(input);
        ASSERT_NOT_NULL(input->pPackager);
        ASSERT_NOT_NULL(input->pszPathName);
    
        CString path_name(input->pszPathName);
    
        BOOL result = input->pPackager->SPackagerDocBase::OnSaveDocument(path_name);
        return result;
    }
    

    如果我取消缝合 WaitForSingleObject(..., 500); 然后线程启动,所有信息都存在,并且没有错误。但如果我删除了这些行,那么SaveDocumentThread中的输入为NULL,所有数据都是零或垃圾。

    有没有办法确保SaveDocumentThread在继续之前已经启动。IE,请等待线程启动,但不要等待指定的时间(500毫秒)。在其他一些计算机上,500毫秒可能不是足够的等待时间。

    有“官方”的方法吗?

    0 回复  |  直到 4 年前
        1
  •  3
  •   Louis Go    4 年前

    这是的问题 scope of variable 。 以下注释指定了局部变量的范围 threadInput

        ProcessDocumentThreadInput threadInput(this, lpszPathName); // <=== threadInput created
        
        if (m_bTempMode)
        {
            m_TempSaveThread = AfxBeginThread(SaveDocumentThread, &threadInput);
            // This fixes the problem, but is considered unstable
            // if (m_TempSaveThread->m_hThread)
            //        WaitForSingleObject(m_TempSaveThread->m_hThread, 500);
            return TRUE; // <=== threadInput destructed
        }
    

    您的变通方法 WaitForSingleObject() 延迟变量的销毁 threadInput 你会看到结果。

    克服局部变量的范围。

    1. 将其存储在类成员变量中。
    2. 将其存储为( 最好聪明一点 )指针和( 最好不要 )处理它的破坏。

    编辑:

    正如@Jabberwocky所说,函数 OnSaveDocument() 可能会被调用两次以上,因为它是由后台线程调用的。

    我建议重构 save() 函数out and let if else 分别称呼他们。

        2
  •  2
  •   Dharman vijay    4 年前

    正如其他人所指出的,问题在于 threadInput 在线程开始之前结束。

    您可以动态分配的实例 ProcessDocumentThreadInput 并将指向该实例的指针传递给线程。

    auto* threadInput = new ProcessDocumentThreadInput(this, lpszPathName);
    ...
    AfxBeginThread(SaveDocumentThread, threadInput);
    

    然而,在这种情况下,释放内存的责任变得很混乱。 既然您在问题中使用了C++11标记,那么您可能需要使用 std::shared_ptr std::unique_ptr 并将其传递给线程,这将使您使用 std::thread 而不是AfxBeginThread。(顺便说一句,我没有使用MFC的经验。)

    BOOL SPackagerDoc::OnSaveDocument( IN LPCTSTR lpszPathName)
    {
        ...
        std::thread t(SaveDocumentThread, std::make_unique<ProcessDocumentThreadInput>(this, lpszPathName));
        ...
    }
    
    ...
    
    StUInt32 SaveDocumentThread(std::unique_ptr<ProcessDocumentThreadInput>&& threadInput)
    {
       ...
    }