【问题标题】:Qt5.2.0; Debian Wheezy: QSqlDatabase destructor causes segfaultQt5.2.0; Debian Wheezy:QSqlDatabase 析构函数导致段错误
【发布时间】:2014-02-17 16:28:49
【问题描述】:

我有一个使用包装类来访问 MySQL 数据库的应用程序。由于在应用程序的不同位置访问数据库,并且应用程序是多线程的,因此使用包装器类来最小化访问数据库所需的重复代码量。

我在某处读到必须在同一个线程中构造、使用和销毁QSqlDatabase。如果您在一个线程中创建连接,然后在另一个线程中破坏它,则可能会出现未记录/不受支持的行为。为了防止这种情况,包装器在设置QSqlQuery 之前创建连接,然后在查询完成时关闭并删除该连接。建立一个连接池并在整个应用程序执行期间保持它们处于活动状态实际上并不可行,因为在任何给定时间都有任意数量的线程在运行,每个线程可能需要异步执行多个数据库任务,因此不完全是线程和数据库连接之间的 1-1 关联开始。

问题是应用程序运行一段时间后,它会因段错误而崩溃,这归咎于QSqlDatabase 析构函数。奇怪的是,该错误仅在应用程序运行一段时间并完成大量查询后才会出现。 (有时它会在 15 分钟后崩溃,有时会在几个小时后崩溃)。最近的堆栈跟踪:

Program received signal SIGSEGV, Segmentation fault.
pthread_cond_timedwait@@GLIBC_2.3.2 ()
    at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:174
174     ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S: No such file or     directory.
(gdb) where
#0  pthread_cond_timedwait@@GLIBC_2.3.2 ()
    at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:174
#1  0x00007ffff32f55b4 in my_thread_global_end ()
   from /opt/Qt/5.2.0/gcc_64/plugins/sqldrivers/libqsqlmysql.so
#2  0x00007ffff32f40a5 in my_end ()
   from /opt/Qt/5.2.0/gcc_64/plugins/sqldrivers/libqsqlmysql.so
#3  0x00007ffff32db5c7 in mysql_server_end ()
   from /opt/Qt/5.2.0/gcc_64/plugins/sqldrivers/libqsqlmysql.so
#4  0x00007ffff32cd806 in ?? ()
   from /opt/Qt/5.2.0/gcc_64/plugins/sqldrivers/libqsqlmysql.so
#5  0x00007ffff32cd829 in ?? ()
   from /opt/Qt/5.2.0/gcc_64/plugins/sqldrivers/libqsqlmysql.so
#6  0x00007ffff7657537 in ?? () from /opt/Qt/5.2.0/gcc_64/lib/libQt5Sql.so.5
#7  0x00007ffff7657e7d in QSqlDatabase::~QSqlDatabase() ()
   from /opt/Qt/5.2.0/gcc_64/lib/libQt5Sql.so.5
#8  0x00007ffff7659204 in ?? () from /opt/Qt/5.2.0/gcc_64/lib/libQt5Sql.so.5
#9  0x00000000004143c2 in DB::~DB (this=0x7ffff0829200,
    __in_chrg=<optimized out>) at db.cpp:32
#10 0x000000000042e758 in WeatherOutcome::MassInsert (
    this=0x7ffff0829550, inputVector=...) at weatheroutcome.cpp:96

这是包装类头文件:

#ifndef DB_H
#define DB_H
#include <QString>
#include <QtSql> 
#include <QSqlQuery>
#include <QSqlError>
#include <QUuid>
class DB
{
public:
    DB();
    ~DB();
    bool SetQuery(QString query);
    bool Exec();
    void manualConnect();
protected:
    QSqlQuery query;
    QSqlDatabase* db;
};

#endif // DB_H

包装类 CPP 文件:

#include db.h
DB::DB()
{

}
DB::~DB()
{
    if (query.isActive())
    {
        query.finish();
        query.clear();
    }
    if (db != NULL)
    {
        QString connName = db->connectionName();
        db->close();
        delete db;
        db = NULL;
        try
        {
            QSqlDatabase::removeDatabase(connName);
        }


        catch(...)
        {

        }
    }
}

void DB::manualConnect()
{
    QUuid uniqueId = QUuid::createUuid();
    QString connectionID = uniqueId.toString();
    QSqlDatabase::addDatabase("QMYSQL", connectionID);

    db = new QSqlDatabase(QSqlDatabase::database(connectionID, false));
    db->setHostName("127.0.0.1");
    db->setDatabaseName("my_db_name");
    db->setUserName("username");
    db->setPassword("password");

    db->open();
}

bool DB::Exec()
{
    return query.exec()  ;
}

bool DB::SetQuery(QString queryString)
{
    manualConnect();
    if (db)
    {
        if (db->isOpen())
        {
            query = QSqlQuery(*db);
            return query.prepare(queryString);
        }
        return false;
    }
    else
        return false;
}

为什么这个相当简单的析构函数会导致段错误并使应用程序崩溃?

【问题讨论】:

    标签: c++ multithreading qt destructor qsqldatabase


    【解决方案1】:

    您是否尝试过使用deleteLater() 而不是调用delete db;

    http://qt-project.org/doc/qt-5.0/qtcore/qobject.html#deleteLater

    您可能还想尝试disconnect db 变量,这样一些待处理的事务在它消失后不会尝试对db 做某事。

    http://qt-project.org/doc/qt-5.0/qtcore/qobject.html#disconnect

    http://qt-project.org/doc/qt-5.0/qtcore/objecttrees.html

    希望对您有所帮助。

    【讨论】:

    • 在 Qt 5.2.0 中,它看起来不像 QSqlDatabase 继承 QObject。所以,我不能调用 deleteLater() 或 disconnect()。当我尝试编译时,它给出了这个:db.cpp: In destructor 'DB::~DB()': db.cpp:28:13: error: 'class QSqlDatabase' has no member named 'disconnect' db.cpp:29:13: error: 'class QSqlDatabase' has no member named 'deleteLater'
    【解决方案2】:

    您似乎没有正确的方法来创建和销毁 QDatabase

    创建方式应该喜欢

    QUuid uniqueId = QUuid::createUuid();
    db = new QSqlDatabase();
    *db = QSqlDatabase::addDatabase("QMYSQL", uniqueId.toString());
    db->setHostName(...);
    db->setDatabaseName(...);
    db->setUserName(...);
    db->setPassword(...);
    

    然后破坏

    db->close();
    delete db;
    QSqlDatabase::removeDatabase(db->connectionName());
    

    【讨论】:

    • UB 引用的已删除对象
    • 是的,我只是复制我的代码并结合 user3233311 代码。应该首先使用 QString 来获取 connectionName。感谢您的评论。
    猜你喜欢
    • 2013-04-30
    • 2012-03-09
    • 1970-01-01
    • 2015-09-22
    • 2020-09-28
    • 2016-01-07
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多