2017-09-13 1 views
0

동시성에 대해 배우려고하고 있으며 grpc 서비스에서 작은 연결 풀을 구현하여 postgres 데이터베이스에 많은 연결을 만들어야합니다.grpc 서비스에서 연결을위한 간단한 스레드 안전 벡터

각 요청마다 새 연결을 만들지 못하도록 기본 connectionPool을 구현하려고합니다. 시작하려면 안전하게 스레드를 만들려고 시도했습니다 std::vector. grpc 서버를 실행할 때 단일 트랜잭션이 만들어진 다음 서버가 차단되지만 계속 진행되고있는 상황을 추론 할 수는 없습니다. 나는 잔뜩 넣어

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 오류 (오류 11)가 발생합니다.

도움을 주시면 감사하겠습니다. 감사.

+1

'surrender()'는 원시 포인터를'pool_'에 넣습니다. 그러면 '연결'은 범위를 벗어나 그 동일한 목적을 파괴합니다. 'pool_'은 매달려있는 포인터를 가지고 끝납니다. 'pool_'을 공유 포인터의 벡터로 시작하는 것이 가장 좋을 것입니다. 또는 더 나은 여전히'unique_ptr's : 설계된대로, 당신은 클라이언트가 포인터를 포기 하겠지만 여전히 사본을 자신을 위해 보관할 위험을 감수해야합니다. –

+0

@IgorTandetnik Thanks. 매우 유용한 연습입니다. –

답변

2

C++의 스마트 포인터는 개체 소유에 관한 것입니다.

개체 소유권은 누가 개체를 삭제할 것인지에 대한 것입니다.

공유 포인터는 누가 삭제하려고하는지, 언제 공유 관심사인지를 의미합니다. "아무도이 개체를 삭제할 수있는 코드가 없습니다"라고 말하면 다시 가져올 수 없습니다.

코드에서 소유권이 공유 된 개체를 가져 와서 SafeVector에 제출하려고합니다. 이것은 허용되지 않습니다. 어쨌든 .get()에 대한 호출로 시도하지만 해당 객체를 삭제할 수있는 권한은 공유 포인터가 소유합니다.

삭제가 진행되고 (어쩌면 당장은 어쩌면 내일) 컨테이너에 삭제 된 객체에 대한 매달린 포인터가 있습니다.


공유 ptrs를 고유 한 ptrs로 변경하십시오. 필요에 따라 이동을 추가하여 컴파일하십시오.

항복 할 때 제공된 고유 한 ptr이 비어 있지 않다는 것을 주장하십시오.

그리고 당신이 거기에 whike,

cv_.notify_one(); 

또한

std::vector<std::unique_ptr<pqxx::connection>> pool_; 

및 변경 것 :

pool_.push_back(std::move(connection)); 

당신이 pool_의 종류를 업데이트하지 않는 경우, 대신 .get().release()으로 변경하십시오. 공유 된 ptr과 달리 고유 ptr은 소유권을 포기할 수 있습니다.