私は同時実行性について学びたいと思いますし、grpcサービスで小さな接続プールを実装しています。この接続プールはpostgresデータベースに多くの接続を行う必要があります。grpcサービスで接続するための単純なスレッドセーフなベクトル
私は基本的なconnectionPoolを実装して、リクエストごとに新しい接続を作成しないようにしています。まず、スレッドセーフstd::vector
を作成しようとしました。 grpcサーバーを実行すると、1つのトランザクションが作成され、サーバーがブロックされますが、何が起こっているのかを判断することはできません。私はの束を入れ
std::shared_ptr<pqxx::connection> c = safeVector_->borrow();
c->perform(SomeTransactorImpl);
safeVector_->surrender(c);
:すべてのヘルプは、次のように私は接続を使用し、私はその後、ServiceA(s)
ServiceA
の内側に私のサービスにSafeVector* s = new SafeVector(4, 10);
を通過し、メインで
class SafeVector {
std::vector<pqxx::connection*> pool_;
int size_;
int max_size_;
std::mutex m_;
std::condition_variable cv_;
public:
SafeVector(int size, int max_size) : size_(size), max_size_(max_size) {
assert(size_ <= max_size_);
for (size_t i = 0; i < size; ++i) {
pool_.push_back(new pqxx::connection("some conn string"));
}
}
SafeVector(SafeVector const&)=delete; // to be implemented
SafeVector& operator=(SafeVector const&)=delete; // no assignment keeps things simple
std::shared_ptr<pqxx::connection> borrow() {
std::unique_lock<std::mutex> l(m_);
cv_.wait(l, [this]{ return !pool_.empty(); });
std::shared_ptr<pqxx::connection> res(pool_.back());
pool_.pop_back();
return res;
}
void surrender(std::shared_ptr<pqxx::connection> connection) {
std::lock_guard<std::mutex> l(m_);
pool_.push_back(connection.get());
cv_.notify_all();
}
};
をいただければ幸いです私は、(1)shared_ptrまたは(2)さまざまなロック構造のコアコンセプトの根本的な誤解を持っていると確信しています。
特に、4接続(私のマシン上のハードウェアスレッドの最大数)を使用した後、borrow()
メソッドで接続を返そうとするとseg fault(エラー11)が発生するようです。
ご協力いただければ幸いです。ありがとう。
'surrender()'は、生のポインタを 'pool_'に戻します。その後、 'connection'は範囲外になり、まったく同じオブジェクトを破壊します。 'pool_'は、ぶら下がっているポインタを保持してしまいます。おそらく 'pool_'を共有ポインタのベクトルで始めるのが最善でしょう。それとも、 'unique_ptr's:設計通り、クライアントがポインタを「降伏させる」というリスクを実行しますが、依然としてコピーを保持します。 @IgorTandetnikありがとう、 –
それは非常に便利なウォークスルーです。 –