2009-11-17 45 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