线程和分叉安全
您应始终为每个线程指定自己的 mongocxx::client
。
一般来说,每个mongocxx::client
对象及其所有子对象(包括mongocxx::client_session
、 mongocxx::database
、 mongocxx::collection
和mongocxx::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();
在上面的示例中,尽管两个数据库是单独同步的,但它们源自同一客户端。 库内存在共享状态,现在正在未经同步的情况下对其进行修改。 如果db2
是db1
的副本,也会出现同样的问题。
可接受的线程示例
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::client
或mongocxx::pool
。 因此,任何客户端或池都必须在分叉之后而不是之前创建。