2013-04-08 24 views
12

Tôi có một lớp chuỗi mà từ đó tôi muốn thỉnh thoảng có được một con trỏ một biến mẫu. Tôi muốn truy cập này được bảo vệ bởi một mutex để thread bị chặn truy cập vào tài nguyên này cho đến khi client kết thúc bằng con trỏ của nó.Làm cách nào để tạo một con trỏ thông minh có thể khóa và mở một mutex?

Cách tiếp cận ban đầu của tôi là trả về một cặp đối tượng: một con trỏ tới tài nguyên và một đối tượng shared_ptr cho đối tượng khóa trên mutex. Điều này shared_ptr giữ tham chiếu duy nhất để các đối tượng khóa để mutex nên được mở khóa khi nó đi ra khỏi phạm vi. Một cái gì đó như thế này:

void A::getResource() 
{ 
    Lock* lock = new Lock(&mMutex); 
    return pair<Resource*, shared_ptr<Lock> >(
     &mResource, 
     shared_ptr<Lock>(lock)); 
} 

Giải pháp này ít hơn lý tưởng vì nó yêu cầu khách hàng giữ toàn bộ cặp đối tượng. Hành vi như thế này phá vỡ sự an toàn chủ đề:

Resource* r = a.getResource().first; 

Bên cạnh đó, thực hiện riêng của tôi về điều này được deadlocking và tôi đang gặp khó khăn xác định lý do tại sao, vì vậy có thể có những thứ khác xảy ra với nó.

Điều tôi muốn có là shared_ptr chứa khóa dưới dạng biến mẫu, ràng buộc nó bằng phương tiện để truy cập tài nguyên. Điều này có vẻ như một cái gì đó mà cần phải có một mô hình thiết kế được thành lập nhưng đã thực hiện một số nghiên cứu tôi ngạc nhiên khi thấy nó khá khó để đi qua.

Câu hỏi của tôi là:

  • Có một thực hiện phổ biến của mô hình này?
  • Có vấn đề gì với việc đặt một mutex bên trong shared_ptr mà tôi xem là ngăn chặn mẫu này lan rộng không?
  • Có lý do chính đáng nào để không triển khai lớp shared_ptr của riêng tôi để triển khai mẫu này không?

(NB Tôi đang làm việc trên một codebase sử dụng Qt nhưng tiếc là không thể sử dụng tăng trong trường hợp này. Tuy nhiên, câu trả lời liên quan đến tăng vẫn là mối quan tâm chung.)

+0

Cả hai câu trả lời từ đinh tán và Jonanthan Wakely đều thú vị và đáng giá sau. Tôi đang đi với Riz chỉ vì nó tốt đẹp để có một số mã chỉnh sửa cộng tác hoàn chỉnh trong một câu trả lời. –

Trả lời

7

Tôi không chắc chắn nếu có bất kỳ triển khai chuẩn, nhưng kể từ khi tôi thích lại thực hiện những thứ không có lý do, đây là một phiên bản nên hoạt động (giả sử bạn không muốn để có thể sao chép con trỏ như vậy):

template<class T> 
class locking_ptr 
{ 
public: 
    locking_ptr(T* ptr, mutex* lock) 
    : m_ptr(ptr) 
    , m_mutex(lock) 
    { 
    m_mutex->lock(); 
    } 
    ~locking_ptr() 
    { 
    if (m_mutex) 
     m_mutex->unlock(); 
    } 
    locking_ptr(locking_ptr<T>&& ptr) 
    : m_ptr(ptr.m_ptr) 
    , m_mutex(ptr.m_mutex) 
    { 
    ptr.m_ptr = nullptr; 
    ptr.m_mutex = nullptr; 
    } 

    T* operator ->() 
    { 
    return m_ptr; 
    } 
    T const* operator ->() const 
    { 
    return m_ptr; 
    } 
private: 
    // disallow copy/assignment 
    locking_ptr(locking_ptr<T> const& ptr) 
    { 
    } 
    locking_ptr& operator = (locking_ptr<T> const& ptr) 
    { 
    return *this; 
    } 
    T* m_ptr; 
    mutex* m_mutex; // whatever implementation you use 
}; 
+0

+1 điều này nên làm điều đó - có thể vô hiệu hóa chuyển nhượng và thêm một nhà xây dựng di chuyển? – stijn

+0

Chắc chắn, bạn có thể vô hiệu hóa chuyển nhượng bằng cách di chuyển phần 'operator =' sang phần riêng tư, nhưng nó sẽ an toàn miễn là các mutex hỗ trợ khóa lồng nhau. Đã thêm hàm tạo di chuyển. – riv

+0

Đã sửa một vài lỗi chính tả; bạn có thể vô hiệu hóa gán với từ khóa '= delete', nhưng nó không được hỗ trợ bởi Visual Studio của tôi. – riv

3

bạn đang mô tả một biến thể của mẫu EXECUTE AROUND POINTER, được mô tả bởi Kevlin Henney trong Executing Around Sequences.

Tôi có triển khai thử nghiệm mẫu tại exec_around.h nhưng tôi không thể đảm bảo nó hoạt động chính xác trong mọi trường hợp vì đó là công việc đang được tiến hành. Nó bao gồm một hàm mutex_around tạo ra một đối tượng và kết thúc nó trong một con trỏ thông minh khóa và mở khóa một mutex khi được truy cập.

Các vấn đề liên quan