Menu Docs
Página inicial do Docs
/ / /
Driver C++

Segurança de threads e forquilhas

Nesta página

  • Exemplo de threading incorreto
  • Exemplo de threading aceitável
  • Exemplo de Threading ideal
  • Segurança de fork

Você deve sempre dar a cada thread seu próprio mongocxx::client.

Em geral, cada objeto mongocxx::client E todos os seus objetos filhos, incluindo mongocxx::client_session, mongocxx::database, mongocxx::collection e mongocxx::cursor, devem ser usados por um único thread de cada vez. Isso vale mesmo para clientes adquiridos de um mongocxx::pool.

Mesmo que você crie vários objetos filho a partir de um único client e sincronize-os individualmente, isso não é seguro, pois eles modificarão simultaneamente as estruturas internas do client. O mesmo vale se você copiar um objeto filho.

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();

No exemplo acima, embora os dois bancos de dados sejam sincronizados individualmente, eles são derivados do mesmo cliente. Há um estado compartilhado dentro da biblioteca que agora está sendo modificado sem sincronização. O mesmo problema ocorre se db2 for uma cópia de 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();

Na maioria dos programas, os clientes serão de longa duração por conveniência e desempenho. Neste exemplo artificial, há um pouco de sobrecarga porque estamos fazendo muito pouco trabalho com cada cliente, mas normalmente essa é a melhor solução.

Nem um mongocxx::client nem um mongocxx::pool podem ser copiados com segurança ao bifurcar. Por esse motivo, qualquer cliente ou pool deve ser criado após a bifurcação, não antes.

Voltar

Configuração e instalação avançadas