我有一个小型聊天应用程序,我使用SQLite数据库存储所有对话。我注意到该应用程序随机冻结,然后我必须最小化并最大化它以使其再次工作。我认为问题可能是导致gui冻结的SQLite选择/插入。我决定尝试将所有SQLite方法移动到一个单独的线程中。
这样做之后,应用程序仍然冻结。
可能值得了解的一些事情:
我直接在QTcpSocket
中使用MainWindow
,但似乎在单独的线程中运行QTcpSocket
没有用处?
我已将SQLite方法分成新线程(请参阅下面的实现)
我使用3 WebViews
来显示我的聊天消息,整个应用程序GUI使用这些WebViews
构建
下面的代码是否真的在一个单独的线程中运行? GUI仍然冻结。
我的标题文件:
class dbThread : public QObject
{
Q_OBJECT
public:
dbThread(QObject* parent);
public slots:
bool openDB(QString agentID);
signals:
void clearPreviousHistory();
private:
QSqlDatabase db;
QHash<QString, QString> countries;
};
我的cpp文件:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);
dbtrad->openDB(userID);
connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
thread->start();
}
dbThread::dbThread(QObject * parent): QObject(parent) {
}
bool dbThread::openDB(QString agentID) {
qDebug() << "OPEN DB FROM THREAD ";
// Find QSLite driver
db = QSqlDatabase::addDatabase("QSQLITE");
// ......
}
这就是我从dbThread
:
MainWindow
方法的方法
dbtrad->getHistory(channelId);
修改
新代码:
// Start database thread
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);
connect(this, SIGNAL(requestOpenDB(QString)), dbtrad, SLOT(openDB(QString)));
thread->start();
emit requestOpenDB(userID);
答案 0 :(得分:2)
dbtrad->openDB(userID);
将像任何正常函数一样执行(为什么要这样?),GUI线程中的 。
moveToThread
允许您在单独的帖子中 执行使用信号 调用的插槽。
如果要在线程中执行openDB
,可以使用
connect (thread, SIGNAL(started()), dbtrad, SLOT(openDBWithUIDAlreadySet()))
或
connect (this, SIGNAL(requestOpenDB(int)), dbtrad, SLOT(openDB(int)))
您需要使用现有或其他信号。 Qthread::start()
发出信号started()
。您也可以定义
MainWindow{
signals:
void requestOpenDB(int);
void queryHistory(int channelid);
}
并使用
手动发出信号emit requestOpenDB(userID); //for openDB
emit queryHistory(channelId); // for getHistory
来自dbThread
对象的响应也需要使用连接到插槽的信号给出。像通知一样。
答案 1 :(得分:0)
QTcpSocket
确实不需要在一个单独的线程中。
只要从创建数据库的那个线程完成所有数据库访问,它也应该没问题
现在到了有趣的部分:我认为你在主线程中创建数据库......通过调用dbtrad->openDB(userId)
答案 2 :(得分:0)
是的,所以qt moveToThread()
没有做你期望它做的事情。您从主线程调用的函数将仅在主线程中执行。该数据库访问导致GUI死机。
moveToThread
仅在单独的线程中移动“事件处理”。这意味着使用dbThread
连接的Qt::QueuedConnection
的任何插槽都将在新线程中执行。
以下方法仅在主ui线程中执行getHistory()
方法。您需要在主线程中创建一个信号,并使getHistory()
成为dbThread
类的一个槽。然后连接两个。
答案 3 :(得分:0)
阅读文档和日志至关重要!!!
在日志中,您有一个警告,如果对象有父母,您可以移动到线程。
另外documentation clearly says that:
更改此对象及其子对象的线程关联。的的 如果对象具有父,则无法移动该对象。事件处理将 继续在targetThread。
<小时/> 正确的解决方法:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
thread = new QThread(this);
dbtrad = new dbThread(); // NO PARENT
dbtrad->moveToThread(thread);
// run object method in thread assigned to this object:
QMetaObject::invokeMethod(dbtrad, "openDB", Qt::QueuedConnection, Q_ARG(QString, userID));
connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
thread->start();
}
MainWindow::~MainWindow()
{
dbtrad->deleteLater();
thread->quit();
thread->wait(5000); // wait max 5 seconds to terminate thread
}