2016-07-27 21 views
12

Tôi có một chức năng trong đó tuyên bố foo phải được thực hiện theo lock_guard nhưng chỉ khi một con trỏ đến một đối tượng mutex đã được cung cấp cho hàm làm tham số. Nếu không, foo không phải là được bảo vệ bởi lock_guard.Sử dụng có điều kiện của tiêu chuẩn :: lock_guard

tôi không có thể sử dụng các lock_guard trong một if vì khóa sẽ được phát hành ngay lập tức khi if khối kết thúc.

như vậy, mã này là vô nghĩa:

bar(std::mutex * optionalMutex = nullptr) 
{ 
    ... 
    if (nullptr != optionalMutex) { 
     std::lock_guard<std::mutex> lockScope(*optionalMutex); 
    }   <- Here the lock ends 

    foo...  <- foo is not protected when optionalMutex was provided 
} 

tôi đã cố gắng một cái gì đó như thế này:

bar(std::mutex * optionalMutex = nullptr) 
{ 
    ... 
    nullptr == optionalMutex ? 0 : std::lock_guard<std::mutex> lockScope(*optionalMutex); 

    // this scope should be protected by lock_guard when optionalMutex was provided 

    foo... 
} 

Nhiều hơn hoặc ít hơn, một trong những giải pháp duy nhất có thể đối với tôi là để lặp lại foo:

bar(std::mutex * optionalMutex = nullptr) 
{ 
    ... 
    if (nullptr != optionalMutex) { 
     std::lock_guard<std::mutex> lockScope(*optionalMutex); 
     foo... 
    } else { 
     foo... 
    } 
} 

Trình biên dịch gcc 4.9.3 không biên dịch ví dụ thứ 2 và tuân thủ ains: error: expected primary-expression before 'lockScope'. Cập nhật: Superlokkus giải thích trong câu trả lời của mình tại sao.

Nhưng tôi muốn tránh bất kỳ mã trùng lặp nào và do đó cũng trùng lặp foo.

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

Có một nhã cách làm thế nào để thực hiện vấn đề này và không sử dụng trùng lặp foo. Tôi biết, tôi có thể sử dụng hàm lambda cho nhómfoo nhưng tôi tò mò nếu có giải pháp khác.

+0

Điều này có vẻ hơi đáng sợ tbh :) – Galik

+0

Bạn đang cố gắng sử dụng chức năng foo với các mutexes khác nhau hoặc chỉ trong một chủ đề an toàn hoặc một cách không an toàn? –

+0

Tôi đoán mọi người lo ngại rằng đây là một Câu hỏi XY (http://meta.stackexchange.com/a/66378): Không thanh gì đó mà các cuộc đua dữ liệu có thể xảy ra? Tại sao là mutex tùy chọn? Bởi vì nó có thể đã được tổ chức? Hay bạn có người đọc/nhà văn? – Superlokkus

Trả lời

9

Làm thế nào về việc này?

void bar(std::mutex * optionalMutex = nullptr) 
{ 
     auto lockScope = (optionalMutex == nullptr) ? 
          std::unique_lock<std::mutex>() 
         : std::unique_lock<std::mutex>(*optionalMutex); 
} 

Giải thích: Trình biên dịch của bạn gặp rắc rối với tuyên bố trước vì bạn không thể thay đổi loại biểu thức thứ ba ?; tức là số 0 theo nghĩa đen không phải là std::lock_guard và ngược lại. Vì vậy, tôi đã thay đổi hai nhánh thành cùng một loại, tại đây std::unique_lock<std::mutex>lock_guard không được thiết kế để sử dụng mà không có một mutex hợp lệ. Nhưng vẫn thích std::lock_guard hơn std::unique_lock trong các trường hợp đơn giản hơn, vì nó sẽ làm cho mã của bạn dễ đọc hơn.

Ngoài ra tuyên bố của bạn không thể thực hiện được cho trình biên dịch, tức là ngay cả cú pháp đúng, vì biến lockScope sẽ chỉ tồn tại trong một nhánh.

+0

Vâng, đây là những gì tôi đang tìm kiếm! Không * giả * 'mutex' và rất tao nhã! Được chấp nhận và upvoted! –

+0

Để tăng sự thanh lịch cho mã của bạn, hãy xem thư viện tùy chọn tăng cường - điều này sẽ cho phép bạn loại bỏ các kiểm tra nullptr xấu xí – Dmitry

+1

Tất nhiên, bạn cũng có thể viết '! OptionalMutex' thay vì' optionalMutex == nullptr', hoặc thậm chí tốt hơn , nghịch đảo toàn bộ điều kiện. – Superlokkus

1

Tôi chỉ có giải pháp này. Sử dụng một đối tượng giả mutex:

Mã này là:

bar(std::mutex * optionalMutex = nullptr) 
{ 
    ... 
    std::mutex dummyMutex; 
    std::lock_guard<std::mutex> lockScope(optionalMutex ? *optionalMutex, dummyMutex); 

    foo...  <- NOW foo is protected when optionalMutex was provided 
} 
8

Điều bạn thực sự có là hai chức năng, một chức năng khóa và một chức năng không hoạt động.Người đầu tiên có thể gọi thứ hai:

void bar() { 
    // whatever 
} 

void bar(std::mutex* mtx) { 
    std::lock_guard<std::mutex> lockScope(*mtx); 
    bar(); 
} 
+0

'Tự động' sẽ giải quyết vấn đề gì? Nó sẽ không phải là một 'std :: mutex' (và những gì bạn thực sự muốn là một' std :: lock_guard')? Và không nên thanh thứ hai thực sự lấy 'std :: mutex' bằng cách tham chiếu (theo kiểu, tôi giả sử, nhưng thực thi rằng bạn không nhận được một' nullptr' được truyền cho bạn)? Nếu người gọi không có một mutex tiện dụng, nó sẽ được gọi 'bar()' anyway. –

+0

@AndreKostur - vâng, tất nhiên, 'auto' là ngớ ngẩn. Cảm ơn. –

+0

@AndreKostur - vâng, có lẽ nên tham khảo, không phải là con trỏ. Điểm của ví dụ, tuy nhiên, là để hiển thị kết nối với mã ban đầu, không phải để tư vấn về phong cách. –

0

Câu trả lời từ Superlockus là đủ tốt, nhưng tôi tự hỏi tại sao bạn không chỉ đơn giản là viết nó như thế:

bar(std::mutex * optionalMutex = nullptr) 
{ 
    if (optionalMutex) 
     optionalMutex->lock(): 

    foo... 

    if (optionalMutex) 
     optionalMutex->unlock(): 
} 

lock_guardunique_lock được thuận tiện nhưng không phải là cách duy nhất.

+7

Trường hợp ngoại lệ và một số lợi nhuận, thân yêu của tôi .... –

+0

Chúng tôi không biết foo, nó có thể là đủ tốt :-) – peroket

1

Đó là một chuôi nhỏ nhưng bạn có thể tránh đi qua các con trỏ liệu bằng cách cho phép người gọi thông qua một std :: unique_lock thay vì:

bar(std::unique_lock<std::mutex> lockScope) 
{ 
    if(lockScope.mutex()) 
    { 
     lockScope.lock(); 
     //do things 
    } 
} 

Điều này có vẻ giống như một biểu hiện rõ ràng hơn về giao diện và làm giảm khả năng lạm dụng.

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