この記事は学習記録です。間違いがあれば著者に連絡し、謙虚に教えてください。
記事ディレクトリ
序文
星を見逃したからには、また月を見逃さないでください。
マルチスレッドおよびデータベース プログラムでは、次の知識が使用されます。
1. マルチスレッド化
1. プロセスとスレッド
プロセスとは: コンピュータ内で独立して実行される多くのプログラムがあり、各プログラムは独立したプロセスを持ち、プロセスは互いに独立して存在します。例: WeChat とブラウザは互いに独立して存在し、スレッドは最小単位
です
。これは、プロセス内に少なくとも 1 つのスレッドが存在することを意味します。
マルチスレッドを理解するために、写真を添付します(B ステーションのチュートリアルから)
(図①)
2. スレッドの同期
スレッドの同期は同時に行われるのではなく、互いに連携・協力して順番に実行されます。あなたが先にやってください、私は後でやります。
スレッド同期とは、複数のスレッドが特定の設定(ミューテックス、イベントオブジェクト、クリティカルセクションなど)によってスレッド間の実行順序を制御すること(いわゆる同期)のことで、スレッド間の同期によって実行順序が確立されるとも言えます。この関係では、同期がない場合、スレッドは独立して実行されます。
図 1 によると、各スレッドの作業は同時に実行されるのではなく、CPU のタイム スライスを取得し、最初に取得した人が最初に実行することがわかります。パブリック リソースを使用する場合、スレッドの同期を確保するためにスレッド ミューテックスが使用されます。
3. スレッド相互排他
スレッド相互排他 (Mutex) は、個々のスレッドがアクセスするときの共有プロセス システム リソースの排他性を指します。多くのスレッドが共有リソースを使用したい場合、その共有リソースを使用できるのは常に 1 つのスレッドだけであり、そのリソースを使用したい他のスレッドは、リソース占有者がリソースを解放するまで待機する必要があります。コードのロックとロック解除でもあります。
ミューテックス オブジェクト: ミューテックス オブジェクトはクリティカル セクションに非常に似ており、ミューテックス オブジェクト メカニズムが採用されており、ミューテックス オブジェクトを所有するスレッドのみがパブリック リソースにアクセスできます。ミューテックス オブジェクトは 1 つだけであるため、共通リソースが複数のスレッドによって同時にアクセスされないことが保証されます。現在ミューテックス オブジェクトを所有しているスレッドは、他のスレッドがリソースにアクセスできるように、タスクの処理後にスレッドを引き渡す必要があります。
二、MYSQL
1.mysqlドライバー
mysql接続時のエラー
QSqlDatabase: QMYSQL driver not loaded
数据库:未加载QMYSQL驱动程序
したがって、プログラムを実行する前に、QT インストール ディレクトリに mysql ドライバーがあることを確認してください。
こちらは Baidu ネットワーク ディスク MYSQL ドライバー (32 ビットおよび 64 ビット)
https://pan.baidu.com/s/1WGZBfGD0K_XTZgmDnQ6ORg\n\nパスワード: qwer
集中!!!!
現在のスレッドによって作成されたデータベース オブジェクトとクエリ オブジェクトは、現在のスレッドでのみ使用でき、スレッドをまたがって使用することはできません。
ここで言われているのは、スレッドによって作成された QSqlDatabase オブジェクトと検出された QSqlQuery オブジェクトは現在のスレッドでのみ使用できるということです。
データベース接続名はさまざまなスレッドで使用できます。
通常、デフォルトの接続名は「qt_sql_default_connection」ですが、ここでは「MySql」に設定します
g_db = QSqlDatabase::addDatabase("QMYSQL", "MySql");//接続ドライバー データベース接続名
マルチスレッドとデータベースのアプリケーションでは、複数のスレッドが同じデータベースを制御する場合、データベース接続名を作成し、メインスレッドとサブスレッドが接続を共有できます。メインスレッドは接続を作成し、その後操作 (クエリなど) が可能になります。子スレッドは、メインスレッドが作成した接続名を取得し、操作(クエリなど)を実行します。
これを使用して、メインスレッドでデータベースを開き、そのデータベースを子スレッドで使用します。
3. メインスレッドがデータベースを開きます
//数据库设置
if(QSqlDatabase::contains("qt_sql_default_connection"))
{
g_db = QSqlDatabase::database("qt_sql_default_connection");
ui->label_Setting->setText("qt_sql_default_connection");
}
else
{
g_db = QSqlDatabase::addDatabase("QMYSQL","MySql");//连接驱动 数据库连接名
ui->label_Setting->setText("QSQLITE");
}
if(g_db.isValid())
{
ui->label_Setting->setText("Valid");
}
else
{
ui->label_Setting->setText("NO Valid");
}
g_db.setHostName(strHost); //主机名
g_db.setPort(iPort); //端口名
g_db.setDatabaseName("test1"); //数据库名称 (默认设置)
g_db.setUserName(strUserName); //用户名 (root)
g_db.setPassword(strPasse); //密码 (root)
g_db.open();
// 创建数据库,把下面的newDataBase替换成你要创建的数据库名字。
QString newDataBase = QString("%1%2").arg("create database if not exists").arg(ui->edit_DBName->text());
g_db.exec(newDataBase);
// 连接上刚刚创建好的数据库,重新设置数据库名称
g_db.setDatabaseName(ui->edit_DBName->text());
// 必须重新调用一遍.open(),这里不加这一句,后面就会报错"QSqlQuery::exec: database not open"
if(!g_db.open())
{
QSqlError slqErr = g_db.lastError();
qDebug()<<"数据库连接失败"<<slqErr;
ui->lb_connState->setText("连接数据库失败");
ui->lb_connState->setStyleSheet("color:red;");
return false;
}
else
{
qDebug() << "当前打开数据库线程id:" << QThread::currentThread();
ui->lb_connState->setText("连接成功");
m_cboard.StartRun(Selete_JustTimeOpenDB);
ui->lb_connState->setStyleSheet("color:green;");
EnableControl(false);
return true;
}
4 番目に、子スレッドはデータベースを使用します。
1. 子スレッドがデータテーブルを作成します。
if(!g_db.open())
{
emit sig_SetTimeONState("数据库未打开",false);
}
else
{
qDebug() << "当前添加数据表线程id:" << QThread::currentThread();
for(int i =0;i< NUM_SENSOR ; i++) //NUM_BOARD
{
QString newSheetName = QString("%1%2").arg("Board").arg(i+1);
QString CreateTable = QString("%1%2%3").arg("create table if not exists ").arg(newSheetName).arg("(Board_id int,Sensor_id int,DL float,DY float,NZ float,GL float,Time varchar(30))");
g_db.exec(CreateTable);
QSqlError slqErr = g_db.lastError();
qDebug()<<"数据表创建失败"<<slqErr;
}
QApplication::processEvents();
}
2. 子スレッドがデータを挿入します。
if(g_db.isOpen() == true)
{
QSqlQuery query = QSqlQuery(g_db); // !!重点
sheetName = QString("%1%2%3%4").arg("insert into ").arg("Board").arg((m_iBoardID+1)).arg("(Board_id,Sensor_id,DL,DY,NZ,GL,Time) values('%1','%2','%3','%4','%5','%6','%7')")
.arg(m_iBoardID+1).arg(i+1).arg(QString::number((double)ALLdata1[i],'f',2)).arg(QString::number((double)ALLdata2[i],'f',2))
.arg(QString::number((double)ALLdata3[i],'f',2)).arg(QString::number((double)ALLdata4[i],'f',2))
.arg(strdatatime);
qDebug()<<"插入数据库信息"<<sheetName;
query.prepare(sheetName);
if(query.exec())
{
qDebug()<<"插入数据到MYSQL成功!";
}
else
{
QSqlError lastError=query.lastError ();
qDebug()<<lastError.driverText ()<<QString(QObject::tr ("失败\n"));
}
}
データベースに接続する接続名が複数ある場合は、クエリ オブジェクトの作成時にどのデータベース接続名を指定する必要があるか、指定しない場合はデフォルトのデータベース接続名になることに注意してください。
QSqlQueryクエリ = QSqlQuery(g_db);
2. 子スレッドがデータテーブルをエクスポートします。
g_Mutex.lock();
qDebug() << "数据库导出表格线程id:" << QThread::currentThread();
if(!g_db.open())
{
emit sig_SetTimeONState("数据库未打开",false);
}
else
{
emit sig_SetTimeONState("数据库已打开",true);
}
QSqlQuery query = QSqlQuery(g_db);
QXlsx::Document xlsx;
QXlsx::Format title_format; /*设置标题的样式*/
QXlsx::Format format2;/*小标题样式*/
QXlsx::Format format3;/*数据内容样式*/
//增加sheet NUM_BOARD
for(int j=0;j< 32;j++)
{
//建立Sheet
QString QStrSheetName;
QStrSheetName = "Light";
QStrSheetName += QString::number(j+1);//j+1
xlsx.addSheet(QStrSheetName);
title_format.setBorderStyle(QXlsx::Format::BorderThin);//外边框
format2.setBorderStyle(QXlsx::Format::BorderThin);//外边框
format3.setBorderStyle(QXlsx::Format::BorderThin);//外边框
xlsx.setRowHeight(1,1,25);/*设置标题行高*/
xlsx.setColumnWidth(1,7,20);/*设置列宽,一共5列参数*/
title_format.setFontSize(11);
title_format.setFontColor(QColor(Qt::red));
title_format.setHorizontalAlignment(QXlsx::Format::AlignHCenter);
title_format.setVerticalAlignment(QXlsx::Format::AlignVCenter);
xlsx.mergeCells("A1:G1",title_format);//合并1~6列写入标题
xlsx.write("A1","TestData Information");
format2.setFontColor(QColor(Qt::blue));
format2.setHorizontalAlignment(QXlsx::Format::AlignHCenter);
xlsx.write("A2", "BoardID", format2);/*写入文字,应该刚才设置的样式*/
xlsx.write("B2", "SensorID", format2);
xlsx.write("C2", "电流", format2);
xlsx.write("D2", "电压", format2);
xlsx.write("E2", "内阻", format2);
xlsx.write("F2", "功率", format2);
xlsx.write("G2", "时间", format2);
format3.setHorizontalAlignment(QXlsx::Format::AlignHCenter);
QString selecMYSQL = QString("%1%2%3%4%5%6%7%8").arg("select * from ").arg("Board").arg(m_iBoardID+1).arg(" where Sensor_id ").arg("in('").arg(j+1).arg("')").arg(";");
qDebug()<<"导出数据库语句信息:"<<selecMYSQL;
query.exec(selecMYSQL);
QSqlError lastError=query.lastError ();
qDebug()<<QString(QObject::tr ("错误\n"))<<lastError.driverText ();
int i=3;
while(query.next())//一行一行遍历
{
xlsx.write(i,1,query.value(0).toInt(),format3);
xlsx.write(i,2,query.value(1).toInt(),format3);
xlsx.write(i,3,query.value(2).toFloat(),format3);
xlsx.write(i,4,query.value(3).toFloat(),format3);
xlsx.write(i,5,query.value(4).toFloat(),format3);
xlsx.write(i,6,query.value(5).toFloat(),format3);
xlsx.write(i,7,query.value(6).toString(),format3);
i++;
}
}
//设置excel表格的默认文件名为"Boardi + 测试数据+ 当前时间"
QString current_date =QDateTime::currentDateTime().toString(Qt::ISODate);
QDateTime datatime=QDateTime::currentDateTime();
QString strdatatime;
strdatatime=datatime.date().toString("yyyy-MM-dd ")+datatime.time().toString("hh.mm.ss");
QString fileName=QString("%1%2%3%4").arg("Board").arg(m_iBoardID+1).arg("测试数据-").arg(strdatatime);
QString dir=QString("/%1").arg(fileName);
QString dir1=dir.replace(QRegExp(":"),"-");
//2.获取应用程序可执行文件的文件.exe路径:
QString path=QCoreApplication::applicationDirPath();/*保存文件的路径*/
QString excelsavepath = QString("%1%2").arg(path).arg(dir1);
QString dtStr=path+dir1+".xlsx";
qDebug()<<"保存路径"<<dtStr;
xlsx.saveAs(dtStr);/*保存*/
sleep(2);
g_Mutex.unlock();
複数の子スレッドがデータベースを操作している場合は、リソースの競合を防ぐために必ずロックを追加してください。
5. MYSQL でのクエリ
– 以下は、データベース内の Student テーブル stuinfo に対するクエリ操作です。
– 1. 学生情報テーブル内のすべての学生の名前と住所をクエリします。
SELECT stuName AS 'name' , stuAddr AS 'address' FROM stuinfo;
– 2. 複数の列の結果を 1 つの列に結合します。
SELECT CONCAT(stuName, '-', stuAddr) AS 'name-address' FROM stuinfo;
– 3. 重複する学生名をフィルタリングします。
SELECT DISTINCT stuName FROM stuinfo;
– 4. データクエリの数を制限する
SELECT * FROM stuinfo LIMIT 3,6;
– 5. 学生のシリアル番号に従って、最も大きいシリアル番号を持つ 3 人の学生をクエリします。
SELECT * FROM stuinfo ORDER BY stuNO DESC LIMIT 0, 3;
– 6. すべての男子をクエリします。
SELECT * FROM stuinfo WHERE stuSex = 'male';
– 7. 番号が 10010 を超えるすべての生徒をクエリします
。 SELECT * FROM stuinfo WHERE stuNO > 10010;
– 8. 雲南省出身でないすべての学生をクエリします
。 SELECT * FROM stuinfo WHERE stuAddr != '雲南';
– 9. 10005 ~ 10015 の数値を使用して生徒にクエリ
を実行します。 SELECT * FROM stuinfo WHERE stuNO >= 10005 AND stuNO <= 10015;
– 10.between および間隔クエリ
SELECT * FROM stuinfo WHERE stuNO BETWEEN 10005 AND 10015;
– 11. データベース内の Zhang San、Li Si、Wang Wu の情報をクエリします。
SELECT * FROM stuinfo WHERE stuName IN('Zhang San', 'Li Si', 'Wang Wu');
– 12. Zhang San、Li Si、Wang Wu 以外の名前のデータベース内の情報を照会します。
SELECT * FROM stuinfo WHERE stuName NOT IN('Zhang San', 'Li Si', 'Wang Wu');
– 13. 姓が王である生徒にクエリを実行します
。 SELECT * FROM stuinfo WHERE stuName LIKE '王_%';
– 14. 姓が Wang で番号が 10005 より大きい情報をクエリします。
SELECT * FROM stuinfo WHERE stuName LIKE '王_%' AND stuNO >= 10005;
– 15. クエリ設定が NULL であるデータはどの演算子でも操作できません– SELECT * FROM stuinfo WHERE stuFav IS NULL; SELECT * FROM stuinfo WHERE stuFav IS NOT NULL;
を決定するために使用できるのは IS のみです。
– 16. すべての生徒の合計スコアをカウントします。
SELECT SUM(stuScore) AS 'TotalScore' FROM stuinfo;
– カウントには COUNT 関数を使用します (名前でカウントする場合、同じ名前の学生は無視します)
– 17. 学生テーブルに何人の学生がいるかをカウントします。
SELECT COUNT(stuNO) FROM stuinfo;
– MAX は列の最小値を見つけるために使用され、MIN はその逆
– 18. 最も高いスコアを持つ生徒のスコアを見つける
SELECT MAX(stuScore) FROM stuinfo;
– 19. 学生テーブルの学生番号の最大差を求める
– SELECT (SELECT MAX(stuNO) FROM stuinfo) - (SELECT MIN(stuNO) FROM stuinfo)
SELECT (MAX(stuNO) - MIN(stuNO)) FROM stuinfo;
– AVG 関数は、平均
– 20 を計算するために使用されます。生徒の平均スコアをクエリします。
SELECT AVG(stuScore) FROM stuinfo;
– 21. スコアが平均より大きい生徒の数を見つけます。
SELECT COUNT(stuNO) FROM stuinfo
WHERE stuScore > (SELECT AVG(stuScore) FROM stuinfo);
– 22. 各州の学生数を地理的グループ別に表示します
。 SELECT stuAddr,COUNT(stuNO) FROM stuinfo
GROUP BY stuAddr;
– 23. 各州の学生の平均スコアを地理的グループ別に表示します
。 SELECT stuAddr,AVG(stuScore) FROM stuinfo
GROUP BY stuAddr;
– 24. 各州のスコアが 60 を超える生徒の数を地域別に表示します。
SELECT stuAddr, COUNT(stuNO) FROM stuinfo
GROUP BY stuAddr, stuScore
HAVING stuScore > 60;
– 複数テーブルの結合クエリで一般的に使用されるのは 3 つの内部結合 (交差) 左結合 () 右結合
– 25. すべての学生情報をクエリ
SELECT * FROM stuinfo, stuclass; – 無条件リンクはデカルト集合を実行します
SELECT * FROM stuinfo, stuclass WHERE stuinfo。 classID = stuclass.classID;
SELECT * FROM stuinfo INNER JOIN stuclass ON stuinfo.classID = stuclass.classID;
– 26. 左結合では、左テーブルのデータが優先されます。左テーブルのデータが右テーブルのデータに対応しない場合は、NULL を使用して右テーブルの列を表示します。 SELECT * FROM stuinfo
LEFT JOIN stuclass ON stuinfo.classID = stuclass.classID;
– 27.右接続
SELECT * FROM stuinfo RIGHT JOIN stuclass ON stuinfo.classID = stuclass.classID;
– 28. 1 年生の全生徒の情報を照会する
SELECT * FROM stuinfo INNER JOIN stuclass ON stuinfo.classID = stuclass.classID
WHERE className LIKE 'first Grade%';
要約する
要約が得意なので、さらに詳しく説明します。