概述
我需要將 CDocument 保存在后臺作業執行緒中。在我們的 MFC 應用程式中有一個點會提示用戶在繼續之前進行保存。正常情況下,他們可以在不保存的情況下繼續,沒有問題。但是,有時我們會在稍后的程序中需要該檔案,因此如果用戶單擊“否”,我們希望在后臺保存檔案的臨時版本,而無需讓用戶等待保存繼續。
問題
當我啟動時AfxBeginThread(SaveDocumentThread, &threadInput),在 SaveDocumentThread 啟動之前,&threadinput 已從記憶體中清除。
代碼
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 毫秒可能不是足夠的等待時間。
有沒有“官方”的方式來做到這一點?
uj5u.com熱心網友回復:
這就是變數范圍的問題。以下注釋指定了區域變數的范圍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您會看到結果。
克服區域變數的作用域。
- 將其存盤在類成員變數中。
- 將其存盤為(最好是聰明的)指標并(最好不要)處理它的破壞。
編輯:
正如@Jabberwocky 所說,函式OnSaveDocument()可能被呼叫兩次以上,因為它是由后臺執行緒呼叫的。
我建議將save()函式重構出來,if然后else分別呼叫它們。
uj5u.com熱心網友回復:
正如其他人所指出的那樣,問題是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)
{
...
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/410163.html
標籤:
