Docs 菜单
Docs 主页
/ / /
C++ 驱动程序

线程和分叉安全

在此页面上

  • 不正确的线程示例
  • 可接受的线程示例
  • 理想的线程示例
  • 货叉安全

您应始终为每个线程指定自己的 mongocxx::client

一般来说,每个mongocxx::client对象及其所有子对象(包括mongocxx::client_sessionmongocxx::databasemongocxx::collectionmongocxx::cursor一次应由一个线程使用。 即使对于从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();

在上面的示例中,尽管两个数据库是单独同步的,但它们源自同一客户端。 库内存在共享状态,现在正在未经同步的情况下对其进行修改。 如果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 。 因此,任何客户端或池都必须在分叉之后而不是之前创建。

后退

教程

来年

连接池