我在主进程中有一个股票列表,我创建了4个线程,试图将它的每一行传递给多个线程。
根据结果,似乎线程没有同步,并且存在重复连接。
但是我已经使用了互斥锁,并且使用了工厂模式来避免重复的连接名。
我不知道发生了什么事。
多谢了!
main.cpp
QThreadPool threadPool;
threadPool.setMaxThreadCount(4);
QMutex mutex;
while (stockListQuery.next()) {
QString name_ = stockListQuery.value("name").toString();
mutex.lock();
testRunable* query = new testRunable(db, name_);
threadPool.start(query);
mutex.unlock();
}
threadPool.waitForDone();
.h
class testRunable : public QRunnable {
public:
testRunable(QSqlDatabase db, const QString &name);
void run() override;
private:
QSqlDatabase db_;
QMutex m_mutex;
QString name_;
};
.cc
testRunable::testRunable(QSqlDatabase db, const QString &name): db_(db), name_(name)
{
}
void testRunable::run() {
QString ids = QString::number((int)QThread::currentThread());
QSqlDatabase db = DBFactory(ids).getDatabase();
int iterations = 0;
while (1) {
m_mutex.lock();
qDebug() << name_ << " " << db.connectionName();
m_mutex.unlock();
if (iterations >= 1) {
break; // stop the loop and exit the thread
}
iterations++;
QThread::sleep(1); // seconds
}
}
更新感谢@user4581301,我修改了下面的互斥锁用法,解决了我的一个问题。哦,上帝。
您可以看到控制台上没有重复的库存。但另一个问题是连接名重复。
我想这和我的工厂模式有关。我把所有的连接都保存在一个Map中,以避免重复连接,我还在弄清楚。
谢谢你们所有人
main.cpp
QThreadPool threadPool;
threadPool.setMaxThreadCount(4);
QMutex mutex;
// Submit the query task to the thread pool
while (stockListQuery.next()) {
QString name_ = stockListQuery.value("name").toString();
testRunable* query = new testRunable(db, name_, mutex); <--- ref of mutex should be passed in the function
threadPool.start(query);
}
threadPool.waitForDone();
对@Mooing Duck来说,你把多个东西放进同一个名字的stockListQuery是什么意思?
.h
class DBFactory {
public:
QSqlDatabase getDatabase();
void setConnetName(const QString& connectName);
private:
QString conName_;
QMap<QString, QSqlDatabase> m_;
};
class testRunable : public QRunnable {
public:
testRunable(QSqlDatabase db, const QString &name, QMutex& mutex);
void run() override;
private:
DBFactory dbFac_;
QSqlDatabase db_;
QMutex& m_mutex;
QString name_;
};
.cpp
QSqlDatabase DBFactory::getDatabase()
{
if (m_.contains(conName_)) {
return m_[conName_];
}
// Create a new database connection object
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", conName_);
db.setDatabaseName("STOCKLIST.db");
if (!db.isOpen()) {
db.open();
}
QSqlQuery query(db);
query.exec("PRAGMA journal_mode=WAL;");
if (query.next()) {
QString mode = query.value(0).toString();
//qDebug() << "Current SQLite mode is:" << mode;
}
// save to map
m_[conName_] = db;
return db;
}
void DBFactory::setConnetName(const QString& connectName)
{
conName_ = connectName;
}
testRunable::testRunable(QSqlDatabase db, const QString &name, QMutex& mutex): db_(db), name_(name), m_mutex(mutex) {
}
void testRunable::run() {
QString ids = QString::number((int)QThread::currentThread());
dbFac_.setConnetName(ids);
QSqlDatabase db = dbFac_.getDatabase();
m_mutex.lock();
qDebug() << name_ << " " << db.connectionName();
stockCrawler(name_);
saveToTable("result_table", db);
m_mutex.unlock();
}
1条答案
按热度按时间t3psigkw1#
正确的方法是像使用任何其他数据库一样使用SQLite数据库,包括涉及多个线程时。让我解释一下。
在[Using SQLite in multi-threaded applications]中,我们可以看到SQLite可以成为线程安全的:
在序列化模式下,查询是逐个执行的。
线程模式的编译时选择:* 使用SQLITE_THREADSAFE编译时参数选择线程模式。如果不存在SQLITE_THREADSAFE编译时参数,则使用序列化模式。*
Qt附带的qsqlite驱动程序使用默认模式编译,即:即序列化。
这对你来说意味着你不必在乎。差不多了
以下示例演示:
假设你从主线程打开了一个连接,当一个辅助线程启动时使用
QSqlDatabase::clone
(重要的是:工作线程内),连接名中包含线程id。如果检索起来更容易,可以将
clone
的结果存储到一个thread_local QSqlDatabase database;
变量中,该变量在一个.cpp
文件中声明。QSQLITE_BUSY_TIMEOUT
的用法。这是唯一可能导致查询失败的原因,因为您使用的是多线程环境。
std::shared_mutex
/QReadWriteLock
提供的规则相似(如果不是完全相同的话)(我没有检查代码以查看它是否实际使用了shared_mutex
):读查询可以同时执行。QMutex
会让线程相互等待,即使它们不必等待,也会对性能造成太大的损害。在下面的示例中:
QSQLITE_BUSY_TIMEOUT
,t2
将失败(在t1
完成之前超时)。query.finish();
(就在实际测试开始之前),t1
将失败。说明:调用
finish()
(或销毁QSqlQuery
)释放互斥锁;失败+因为query
永远不会超出作用域,互斥锁由主线程保存,并且超过t1
。请注意,如果
t2
也可能失败,也可能不失败,这取决于t1
何时“让路”。query.finish();
注解+你删除了t1
内部的lambda体,那么t2
将执行,尽管主线程持有互斥量。