我有一个多线程应用程序,它使用线程池,因此有10个线程运行相同的run()函数,如下所示:
run(){
...
SetTileAt(p, tile);
...
ClearPointsNotIn(list);
...
}
void TileMatrix::ClearPointsNotIn(QList<Point>list)
{
removals.clear();
mutex.lock();
foreach(Point p, matrix.keys())
{
if(!list.contains(p))
{
removals.append(p);
}
}
mutex.unlock();
foreach(Point p,removals)
{
Tile* t=TileAt(p);
if(t!=0)
{
mutex.lock();
delete t;
t=0;
matrix.remove(p);
mutex.unlock();
}
}
removals.clear();
}
void TileMatrix::SetTileAt(const Point &p, Tile* tile)
{
mutex.lock();
Tile* t=matrix.value(p,0);
if(t!=0)
delete t;
matrix.insert(p,tile);
mutex.unlock();
}
Tile* TileMatrix::TileAt(const Point &p)
{
Tile* ret;
mutex.lock();
ret=matrix.value(p,0);
mutex.unlock();
return ret;
}
当我运行应用程序时,它有时会在删除部分崩溃,我已经检查了那时的t值,看起来虽然t!= 0,但是指向的内容完全是垃圾。 我天真地猜测这是一个“删除已删除的指针问题”。 但是我不太确定这是怎么发生的,我怎么能修改代码来阻止呢? 请注意,TileAt中的muetex与ClearPointsNotIn()中的muetex可能会产生死锁......
答案 0 :(得分:2)
这是可能发生的事情,似乎有时会发生:
很有可能在t
中获得指针TileMatrix::ClearPointsNotIn
之后,另一个锁定mutex.lock();
TileMatrix::SetTileAt
函数内的线程。此时,matrix.value(p,0);
可以返回与之前控制TileAt(p);
的线程中返回的TileMatrix::ClearPointsNotIn
完全相同的指针。然后,您删除t
中的TileMatrix::SetTileAt
并解锁。同时在运行TileMatrix::ClearPointsNotIn
函数的线程中,您在t
中已经删除了指针(因为您在另一个线程中删除了它),当您在其上调用delete
时应用程序崩溃
这称为race condition。
我建议您在mutex.lock()
之后TileMatrix::ClearPointsNotIn
之前移动foreach
语句Tile* t=TileAt(p);
。我还建议您删除指针后,还要为其指定0
或NULL
。因此,如果指针之前设置为if(t!=0)
或if
,那么0
将阻止执行线程执行NULL
内的块。有关详细信息,请阅读here和here。
如果不够清楚,请告诉我,我可以提供更多详情。
答案 1 :(得分:1)
正如liuliu所提到的,由于互斥锁对TileAt
的保护不足,你会遇到竞争状态。
ClearPointsNotIn
应完全受互斥锁的保护。点列表也应该通过const引用传递,而不是通过值传递。你应该使用RAII互斥锁柜。它可能如下所示:
void TileMatrix::ClearPointsNotIn(const QList<Point> & list)
{
removals.clear();
QMutexLocker lock(&mutex);
foreach(Point p, matrix.keys())
{
if (!list.contains(p)) removals << p;
}
foreach(Point p, removals)
{
Tile* t = TileAt(p);
delete t;
if (t) matrix.remove(p);
}
}
此外,假设删除图块没有需要保留互斥锁的副作用,您可以重构从互斥锁下删除图块删除:
class TileMatrix {
...
QList<Tile*> deadTiles;
...
};
// Variant 1
void TileMatrix::ClearPointsNotIn(const QList<Point> & list)
{
QMutexLocker lock(&mutex);
deadTiles.clear();
foreach(Point p, matrix.keys())
{
if (list.contains(p)) continue;
Tile* tile = TileAt(p);
if (tile) {
deadTiles << tile;
matrix.remove(p);
}
}
lock.unlock();
foreach(Tile* tile, deadTiles) delete tile;
}
// Variant 2
void TileMatrix::ClearPointsNotIn(const QList<Point> & list)
{
QMutexLocker lock(&mutex);
foreach(Point p, matrix.keys())
{
if (list.contains(p)) continue;
Tile* tile = TileAt(p);
if (!tile) continue;
matrix.remove(p);
delete tile;
}
}
您应该分析变量1是否确实比变体2更快。