刹那(せつな)の瞬き

Willkömmen! Ich heiße Setsuna. Haben Sie etwas Zeit für mich?

std::threadを利用してnanodbcからクエリを実行する

現在 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;
        // thread #1
        auto th1 = std::thread([&conn_str] {
            std::cout << "-- thread #1" << std::endl;
            //std::this_thread::sleep_for(std::chrono::milliseconds(800));
            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;
            }
        });
        // thread #2
        auto th2 = std::thread([&conn_str] {
            std::cout << "-- thread #2" << std::endl;
            //std::this_thread::sleep_for(std::chrono::milliseconds(500));
            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(…等としてみましたが問題ありませんでした。

スレッド生成と接続生成、両方のコストを加味してもなかなか良い感じです。