Docs Menu
Docs Home
/ / /
C++ ドライバー

スレッドとフォークの安全性

項目一覧

  • 誤ったスレッドの例
  • 許容スレッドの例
  • 理想的なスレッド化の例
  • フォークの安全性

常に各スレッドに独自の mongocxx::clientを付与する必要があります。

一般的に、各mongocxx::clientオブジェクトとその子オブジェクト( mongocxx::client_sessionmongocxx::databasemongocxx::collectionmongocxx::cursorなど)は、一度に 1 つのスレッドで使用する必要があります。 これは、 mongocxx::poolから取得されたクライアントにも当てはまります。

単一のclientから複数の子オブジェクトを作成し、個別に同期しても、 clientの内部構造が同時に変更されるため安全ではありません。 子オブジェクトをコピーする場合も同様です。

mongocxx::instance instance{};
mongocxx::uri uri{};
mongocxx::client c{uri};
auto db1 = c["db1"];
auto db2 = c["db2"];
std::mutex db1_mtx{};
std::mutex db2_mtx{};
auto threadfunc = [](mongocxx::database& db, std::mutex& mtx) {
mtx.lock();
db["col"].insert_one({});
mtx.unlock();
};
// BAD! These two databases are individually synchronized, but they are derived from the same
// client, so they can only be accessed by one thread at a time
std::thread t1([&]() { threadfunc(db1, db1_mtx); threadfunc(db2, db2_mtx); });
std::thread t2([&]() { threadfunc(db2, db2_mtx); threadfunc(db1, db1_mtx); });
t1.join();
t2.join();

上記の例では、2 つのデータベースが個別に同期されていますが、これらは同じクライアントから取得されています。 ライブラリ内に共有状態があり、同期せずに変更されています。 db2db1のコピーである場合も、同じ問題が発生します。

mongocxx::instance instance{};
mongocxx::uri uri{};
mongocxx::client c1{uri};
mongocxx::client c2{uri};
std::mutex c1_mtx{};
std::mutex c2_mtx{};
auto threadfunc = [](std::string dbname, mongocxx::client& client, std::mutex& mtx) {
mtx.lock();
client[dbname]["col"].insert_one({});
mtx.unlock();
};
// These two clients are individually synchronized, so it is safe to share them between
// threads.
std::thread t1([&]() { threadfunc("db1", c1, c1_mtx); threadfunc("db2", c2, c2_mtx); });
std::thread t2([&]() { threadfunc("db2", c2, c2_mtx); threadfunc("db1", c1, c1_mtx); });
t1.join();
t2.join();
mongocxx::instance instance{};
mongocxx::pool pool{mongocxx::uri{}};
auto threadfunc = [](mongocxx::client& client, std::string dbname) {
auto col = client[dbname]["col"].insert_one({});
};
// Great! Using the pool allows the clients to be synchronized while sharing only one
// background monitoring thread.
std::thread t1 ([&]() {
auto c = pool.acquire();
threadfunc(*c, "db1");
threadfunc(*c, "db2");
});
std::thread t2 ([&]() {
auto c = pool.acquire();
threadfunc(*c, "db2");
threadfunc(*c, "db1");
});
t1.join();
t2.join();

ほとんどのプログラムでは、便宜上とパフォーマンスのためにクライアントが長時間使用されます。 この変換された例では、各クライアントでの作業がほとんどないため、かなりのオーバーヘッドが存在しますが、通常はこれがベストソリューションです。

フォーク時に、 mongocxx::clientmongocxx::poolも安全にコピーできません。 このため、クライアントまたはプールは、フォーク前ではなく、フォークに作成する必要があります。

戻る

高度な構成とインストール