2013-07-18 29 views
5

Câu hỏi đơn giản - về cơ bản, tôi có phải mở khóa một mutex hay chỉ đơn giản là tôi có thể sử dụng toán tử phạm vi và mutex sẽ tự động mở khóa?Có một mutex mở khóa khi nó ra khỏi phạm vi?

ví dụ:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++; 
} // my mutex is now unlocked? 

hay tôi nên:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++; 
    pthread_mutex_unlock (&myMutex); 
} 
+5

Bạn đang sử dụng triển khai mutex nào? Trừ khi bạn đang thực hiện mutex hỗ trợ RAII, bạn có thể sẽ phải mở khóa một cách rõ ràng mutex. – Void

+0

Cảm ơn @Void. Tôi đang sử dụng pthread.h. Tức là pthread_mutex_unlock() và pthread_mutex_lock(). RAII là gì? –

+3

@AmitNayar: Xem http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization.Nó có thể là thành ngữ quan trọng nhất trong C++, vì hầu như không thể quản lý bộ nhớ và các tài nguyên động khác một cách chính xác mà không có nó. –

Trả lời

16

Mutex không nằm ngoài phạm vi trong ví dụ của bạn; và không có cách nào để trình biên dịch biết rằng một hàm cụ thể cần gọi ở cuối phạm vi, vì vậy ví dụ đầu tiên không không phải là mở khóa mutex.

Nếu bạn đang sử dụng (dễ bị lỗi) các chức năng khóa và mở khóa mutex, thì bạn sẽ cần phải đảm bảo rằng bạn luôn gọi unlock() - ngay cả khi thao tác được bảo vệ ném ngoại lệ.

Cách tốt nhất để làm điều này là sử dụng một lớp RAII để quản lý các khóa, như bạn sẽ cho bất kỳ tài nguyên khác mà cần phát hành sau khi sử dụng:

class lock_guard { 
public: 
    explicit lock_guard(mutex & m) : m(m) {mutex_lock(m);} 
    ~lock_guard() {mutex_unlock(m);} 

    lock_guard(lock_guard const &) = delete; 
    void operator=(lock_guard &) = delete; 

private: 
    mutex & m; 
}; 

// Usage 
{ 
    lock_guard lock(myMutex); 
    shared_resource++; 
} // mutex is unlocked here (even if an exception was thrown) 

Trong C hiện đại ++, sử dụng std::lock_guard hoặc std::unique_lock cho điều này.

2

Sử dụng phương pháp phạm vi RAII là tốt hơn nhiều vì nó đảm bảo rằng các mutex sẽ luôn luôn được mở khóa thậm chí khi đối mặt với trường hợp ngoại lệ hoặc đầu trở về.

Nếu bạn có quyền truy cập vào C++ 11 mặc dù bạn có thể xem xét sử dụng std::atomic<int> thay vào đó trong trường hợp đó bạn không cần phải khóa nó để tăng.

2

Trong trường hợp này, không có mutex sẽ không được mở khóa khi mã này nằm ngoài phạm vi.

Tủ khóa Mutex sau RAII sử dụng thực tế là trình phá hủy được tự động gọi khi đối tượng được phân bổ không được cấp phát vượt quá phạm vi. Sau đó, nó mở khóa mutex khi đối tượng đã khóa mutex không nằm trong phạm vi. Trong trường hợp mã của bạn, không có đối tượng nào được cấp phát trong phạm vi dấu ngoặc ôm, do đó không có khả năng mở khóa mutex khi phạm vi kết thúc.

Ví dụ, sử dụng QMutexLocker từ các thư viện Qt, bạn có thể đảm bảo rằng mutex bạn được mở khóa khi phạm vi được kết thúc:

{ 
    QMutexLocker locker(myMutex); 
    if(checkSomething()) 
    { 
     return; 
    } 
    doSomething(); 
} 

Mã này cũng tương tự như:

{ 
    mutex_lock(myMutex); 
    if(checkSomething()) 
    { 
     mutex_unlock(myMutex); 
     return; 
    } 
    doSomething(); 
    mutex_unlock(myMutex); 
} 

Mặc dù như Brian Neal chỉ ra, nó không xử lý an toàn trường hợp các trường hợp ngoại lệ là checkSomething()doSomething().

Cách thay thế cho Qt's QMutexLocker sẽ là số std::lock_guard của STD.

+2

Hai đoạn mã không tương đương nếu bạn xem xét ngoại lệ. Để làm cho chúng tương đương, bạn phải thêm các khối try/catch xung quanh 'checkSomething' và' doSomething'. Chỉ là một lý do khác để thích ví dụ đầu tiên của bạn sử dụng RAII. :) –

+0

Điểm tốt, @BrianNeal. Tôi đã cập nhật câu trả lời. Tôi làm cho một điểm không sử dụng ngoại lệ, vì chúng không thực sự cần thiết với cơ sở hạ tầng 'Qt', vì vậy tôi dễ dàng quên rằng những người khác cần phải tính đến chúng. –

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