2009-11-17 73 views
2

我有一個奇怪的問題,真的不明白髮生了什麼事情。MFC多線程與刪除[],dbgheap.c

我讓我的應用程序使用MFC多線程多線程。

一切運作良好,到目前爲止,但現在:

某處在代碼的開頭我創建線程:

  m_bucketCreator = new BucketCreator(128,128,32); 
    CEvent* updateEvent = new CEvent(FALSE, FALSE); 
    CWinThread** threads = new CWinThread*[numThreads]; 
    for(int i=0; i<8; i++){ 
     threads[i]=AfxBeginThread(&MyClass::threadfunction, updateEvent); 
     m_activeRenderThreads++; 
    } 

這將創建8個線程此功能工作:

UINT MyClass::threadfunction(LPVOID params) //executed in new Thread 
{ 

Bucket* bucket=m_bucketCreator.getNextBucket(); 

    ...do something with bucket... 

delete bucket; 

} 

m_bucketCreator是一個靜態成員。現在我在Bucket的解構器中得到了一些線程錯誤,試圖刪除一個緩衝區(但是,我明白這個緩衝區應該在這個線程的內存中,所以我不明白爲什麼會有錯誤)。在嘗試delete[] buffer時,錯誤發生在dbgheap.c_CrtIsValidHeapPointer()中。

視覺工作室輸出,它捕獲一停止點該消息並且這可以是由於堆損壞或因爲用戶按下F12(I沒有;))

class BucketCreator { 
public: 
    BucketCreator(); 

~BucketCreator(void); 

void init(int resX, int resY, int bucketSize); 

Bucket* getNextBucket(){ 

Bucket* bucket=NULL; 
//enter critical section 
CSingleLock singleLock(&m_criticalSection); 
singleLock.Lock(); 

int height = min(m_resolutionY-m_nextY,m_bucketSize); 
int width = min(m_resolutionX-m_nextX,m_bucketSize); 

bucket = new Bucket(width, height); 

//leave critical section 
singleLock.Unlock(); 
return bucket; 
} 

private: 

int m_resolutionX; 
int m_resolutionY; 
int m_bucketSize; 

int m_nextX; 
int m_nextY; 

//multithreading: 
CCriticalSection m_criticalSection; 
}; 

和類剷鬥:

class Bucket : public CObject{ 
DECLARE_DYNAMIC(RenderBucket) 
public: 

Bucket(int a_resX, int a_resY){ 

resX = a_resX; 
resY = a_resY; 
buffer = new float[3 * resX * resY]; 

int buffersize = 3*resX * resY; 
for (int i=0; i<buffersize; i++){ 
    buffer[i] = 0; 
} 
} 

~Bucket(void){ 
delete[] buffer; 
buffer=NULL; 
} 


int getResX(){return resX;} 
int getResY(){return resY;} 
float* getBuffer(){return buffer;} 

private: 
int resX; 
int resY; 
float* buffer; 

Bucket& operator = (const Bucket& other) { /*..*/} 
Bucket(const Bucket& other) {/*..*/} 
}; 

有誰能告訴我這裏可能是什麼問題?

編輯:這是我從線程調用的其他靜態函數。這是安全的嗎?

static std::vector<Vector3> generate_poisson(double width, double height, double min_dist, int k, std::vector<std::vector<Vector3> > existingPoints) 
{ 
    CSingleLock singleLock(&m_criticalSection); 
    singleLock.Lock(); 

    std::vector<Vector3> samplePoints = std::vector<Vector3>(); 

      ...fill the vector... 

      singleLock.Unlock(); 
      return samplePoints; 
    } 
+0

爲什麼要對關鍵部分進行鎖定以分配新的存儲桶對象? getNextBucket如何被另一個線程重新輸入?你在試圖同步訪問什麼? – 2009-11-17 02:15:25

+0

我添加了代碼,我在頂部創建線程。 m_bucketCreator是一個由多線程訪問的靜態成員變量 – Mat 2009-11-17 02:21:00

回答

2

所有以前的回覆都是正確的。對於複製構造函數,確保它不會複製緩衝區指針,否則會導致問題。它需要分配一個新的緩衝區,而不是指針值,這會導致'刪除'中的錯誤。但是我不覺得複製構造函數會在你的代碼中被調用。

我已經看過了代碼,我沒有看到任何錯誤。注意在這個GetNextBucket代碼中甚至不需要線程同步,因爲它返回一個局部變量,並且它們是線程前置的。

ValidateHeapPointer中的錯誤是因爲某些東西損壞了堆,當指針寫入內存塊時會發生錯誤。通常這是一個for()循環太過分了,緩衝區沒有足夠的分配,等等。

在調用'delete'期間報告錯誤,因爲這是在驗證調試中的錯誤模式。然而,錯誤發生在那個時間之前,只是在'新'和'刪除'中檢查堆。此外,它不一定與「桶」類有關。

你需要找到這個缺陷,短缺使用像BoundsChecker或HeapValidator這樣的工具,會在你的代碼的部分註釋掉,直到它消失,然後你會發現有問題的代碼。

還有另一種縮小問題的方法。在調試模式下,包含在您的代碼中,並在各個感興趣的點上調用_CrtCheckMemory()。這會在堆損壞時產生錯誤。只需在您的代碼中調用調用以縮小腐敗開始發生的時間點。

我不知道您使用的是哪個版本的Visual C++。如果您使用的是早期版本的VC++ 6.0,請確保在編譯器選項中使用了C運行時庫的Multitreaded DLL版本。

+0

hey! 非常感謝你!_CrtCheckMemory()幫助我追蹤堆損壞的地方,確實有一個prolbem在計算循環中的索引! – Mat 2009-11-17 05:49:45

0

您還沒有製作私人拷貝構造函數或任何默認構造函數。如果class Bucket是通過這些隱式定義的方法之一構造的,則緩衝區可以是未初始化的,也可以是由複製構造函數創建的複製指針。

類Bucket的拷貝構造函數是Bucket(const Bucket &B) - 如果你沒有顯式聲明一個拷貝構造函數,編譯器會爲你生成一個「天真」的拷貝構造函數。

特別是,如果此對象被分配,返回或以其他方式複製,則複製構造函數會將指針複製到新對象。最終,兩個對象的析構函數都會嘗試刪除[]相同的指針,第二次嘗試將是雙重刪除,這是一種堆損壞。

我建議你做class Bucket的拷貝構造函數私有的,這將導致試圖拷貝構造而生成編譯錯誤。作爲替代,您可以實現複製構造函數,爲複製的buffer分配新空間。

與賦值運算符operator=完全相同。

需要一個拷貝構造函數是在Scott Meyer's excellent book, Effective C++: 55 Specific Ways to Improve Your Programs and Designs 55個竅門之一:
Image of the book http://ecx.images-amazon.com/images/I/51WCFVFEB2L._SL500_AA240_.jpg

這本書應該需要閱讀所有的C++程序員。

如果添加:

 
class Bucket { 
/* Existing code as-is ... */ 
private: 
    Bucket() { buffer = NULL; } // No default construction 
    Bucket(const Bucket &B) { ; } // No copy construction 
    Bucket& operator= (const Bucket &B) {;} // No assignment 
} 

並重新編譯,你可能會發現你的問題。

也有另一種可能:如果你的代碼中包含的newdelete其他用途,則有可能分配內存的這些其他用途破壞鏈表結構定義了堆內存。在致電delete期間檢測到這種損壞是很常見的,因爲delete必須利用這些數據結構。

+0

抱歉,這是一個打字錯誤。構造函數在那裏 - 我只是把它命名爲錯誤的。現在我糾正了錯誤 – Mat 2009-11-17 02:54:17

+0

在這種情況下,複製構造函數是Bucket(const類Bucket&B) – 2009-11-17 03:02:25

+0

嗨 - 我試過了,問題仍然完全相同 – Mat 2009-11-17 03:27:10

0

您正在構建RenderBucket。你確定你從那裏調用'Bucket'類的構造函數嗎?它應該是這樣的:

class RenderBucket : public Bucket { 
    RenderBucket(int a_resX, int a_resY) 
    : Bucket(a_resX, a_resY) 
    { 
    } 
} 

初始值設定在鬥類緩衝區設置爲NULL是個好主意......也使默認的構造函數和拷貝構造函數私人將有助於使雙重肯定這些都不是正在使用。記住..如果你不這樣做,編譯器會自動創建這些內容:

Bucket(); <-- default constructor 
Bucket(int a_resx = 0, int a_resy = 0) <-- Another way to make your default constructor 
Bucket(const class Bucket &B) <-- copy constructor 
+0

該死的 - 同樣的複製粘貼錯誤再次 - 對不起:(它應該閱讀新的桶 – Mat 2009-11-17 03:28:27

+0

如果你簡化了這個問題,它可能會消失...你的'...'與桶做什麼可能是你的問題的根源 - 這很難說!雖然懷疑構造函數沒有初始化緩衝區變量的內存,但如果它在析構函數的刪除上失敗,那麼它是有意義的。 – Kieveli 2009-11-17 03:33:39

+0

哦,嘿,關於複製構造函數的好處,在我發佈相同的東西后只有42分鐘。 LOL – 2009-11-17 03:42:15