由于在c ++ Qt多线程应用程序中删除指针而导致崩溃

时间:2014-10-22 17:38:48

标签: c++ multithreading qt

我有一个多线程应用程序,它使用线程池,因此有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可能会产生死锁......

2 个答案:

答案 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);。我还建议您删除指针后,还要为其指定0NULL。因此,如果指针之前设置为if(t!=0)if,那么0将阻止执行线程执行NULL内的块。有关详细信息,请阅读herehere

如果不够清楚,请告诉我,我可以提供更多详情。

答案 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更快。