스레드 및 포크 안전성
항상 각 스레드에 고유한 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
모두 안전하게 복사할 수 없습니다. 이 때문에 모든 클라이언트 또는 풀은 이전이 아닌 포크 이후 에 생성되어야 합니다.