現在 Linux 環境の ODBC 接続では SQL_ATTR_ASYNC_ENABLE に SQL_ASYNC_ENABLE_ON を設定しても機能しません。※iODBC や商用ドライバは調べてません。
そして nanodbc の async_ 系関数も Linux 環境では実装されてません。
WaitForSingleObject()とか無いもんね。残念です。
その一方で、 Linux 環境の ODBC ドライバでも別スレッドを立ち上げれば SQL 文を並行して実行可能との事。ODBC ドライバ側で直列化されるとしても、です。
非同期実行等がダメでも、それはそれ。
スレッドを立てて全体の処理時間を効率良く短縮できるなら...と、引き続き nanodbc で試してみました。
とりあえずスレッドを 2 つ用意して、クエリを実行してみます。
・main.cpp
#include <iostream>
#include <future>
#include <chrono>
#include <nanodbc/nanodbc.h>
int main()
{
std::locale::global(std::locale(""));
std::string conn_str = "Driver={ODBC Driver 17 for SQL Server};Server=localhost;UID=sa;PWD=abcd1234$;Database=my_test_db;";
auto start = std::chrono::system_clock::now();
try
{
std::cout << "-- 処理開始" << std::endl;
auto th1 = std::thread([&conn_str] {
std::cout << "-- thread #1" << std::endl;
nanodbc::connection conn(conn_str);
nanodbc::string sql_text(NANODBC_TEXT("SELECT コード,氏名,入社日 FROM 社員 WHERE コード>199 ORDER BY コード DESC"));
nanodbc::result rs = nanodbc::execute(conn, sql_text);
for (const auto& row : rs) {
auto val_int = row.get<int>("コード");
auto val_str = row.get<nanodbc::string>("氏名");
auto val_date = row.get<nanodbc::string>("入社日");
std::cout << "th#1 " << val_int << " | " << val_str << " | " << val_date << std::endl;
}
});
auto th2 = std::thread([&conn_str] {
std::cout << "-- thread #2" << std::endl;
nanodbc::connection conn(conn_str);
nanodbc::string sql_text(NANODBC_TEXT("SELECT コード,氏名,入社日 FROM 社員 WHERE コード>199 ORDER BY 入社日 DESC"));
nanodbc::result rs = nanodbc::execute(conn, sql_text);
for (const auto& row : rs) {
auto val_int = row.get<int>("コード");
auto val_str = row.get<nanodbc::string>("氏名");
auto val_date = row.get<nanodbc::string>("入社日");
std::cout << "th#2 " << val_int << " | " << val_str << " | " << val_date << std::endl;
}
});
std::cout << "-- 途中で何かの処理開始" << std::endl;
std::cout << "-- 処理中 (1秒くらい)" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "-- 途中で何かの処理終了" << std::endl;
th1.join();
th2.join();
std::cout << "-- 処理終了" << std::endl;
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
auto end = std::chrono::system_clock::now();
std::cout << "経過時間(ミリ秒):"
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< std::endl;
return EXIT_SUCCESS;
}
std::thread に渡すラムダ式でキャプチャしているのは接続文字列です。
nanodbc::connection はスレッド内で生成する必要があります。
・CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(odbc_thread CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(nanodbc REQUIRED)
add_executable(odbc_thread main.cpp)
target_link_libraries(odbc_thread PRIVATE pthread nanodbc)
実行結果
-- 処理開始
-- thread #1
-- 途中で何かの処理開始
-- 処理中 (1秒くらい)
-- thread #2
th#1 th#2 307207 | | 松沢 誠一小川 さよ子 | | 1994-04-011987-04-01
th#2 210 | 成宮 真紀 | 1991-04-01
th#1 305 | 青木 俊之 | 1988-04-01
th#2 204 | 川村 匡 | 1990-04-01
th#1 304 | 山本 雅治 | 1989-04-01
th#2 304 | 山本 雅治 | 1989-04-01
th#1 210 | 成宮 真紀 | 1991-04-01
th#2 305 | 青木 俊之 | 1988-04-01
th#1 207 | 松沢 誠一 | 1994-04-01
th#2 307 | 小川 さよ子 | 1987-04-01
th#1 204 | 川村 匡 | 1990-04-01
-- 途中で何かの処理終了
-- 処理終了
経過時間(ミリ秒):1000
拍子抜けするくらいあっさりと意図した結果になりました。
スレッドを待機させても大丈夫でした。
この後、ソースを書き換えて、std::vector<std::thread>
みたいなコンテナを用意して、1000個くらいpush_back(std::thread(…
等としてみましたが問題ありませんでした。
スレッド生成と接続生成、両方のコストを加味してもなかなか良い感じです。