2013-04-08 25 views
13

Tôi có cần đồng bộ hóa std::condition_variable/condition_variable_any::notify_one không?Tôi có cần phải đồng bộ hóa std :: condition_variable/condition_variable_any :: notify_one

Theo tôi có thể thấy, nếu mất thông báo là chấp nhận được - bạn có thể gọi số notify_one không được bảo vệ (ví dụ như mutex).

Ví dụ, tôi thấy sau thói quen sử dụng (xin lỗi, không nhớ ở đâu):

{ 
    { 
     lock_guard<mutex> l(m); 
     // do work 
    } 
    c.notify_one(); 
} 

Nhưng, tôi kiểm tra libstdC++ nguồn khác nhau, và tôi thấy:

condition_variable::notify_one

void condition_variable::notify_one() noexcept 
{ 
    int __e = __gthread_cond_signal(&_M_cond); 
    // XXX not in spec 
    // EINVAL 
    if (__e) 
     __throw_system_error(__e); 
} 

condition_variable_any::notify_one:

void condition_variable_any::notify_one() noexcept 
{ 
    lock_guard<mutex> __lock(_M_mutex); 
    _M_cond.notify_one(); 
} 

Và đây là cách bố trí của condition_variable_any:

class condition_variable_any 
{ 
    condition_variable _M_cond; 
    mutex _M_mutex; 
    // data end 

Tức là nó chỉ là bao bọc mỏng xung quanh condition_variable + mutex.

Vì vậy, câu hỏi:

  1. Có thread-an toàn để bảo vệ không notify_one bởi mutex cho một trong hai condition_variable_any hoặc condition_variable?
  2. Tại sao việc triển khai điều kiện_variable_any sử dụng mutex bổ sung?
  3. Tại sao việc triển khai condition_variable_any::notify_onecondition_variable::notify_one khác nhau? Có lẽ condition_variable::notify_one yêu cầu bảo vệ thủ công nhưng condition_variable_any::notify_one thì không? Có lỗi libstdC++ không?

Trả lời

13

I.e. nó chỉ là bao bọc mỏng xung quanh condition_variable + mutex.

Er, no. Chỉ vì nó có các thành viên của những loại đó không làm cho nó trở thành một wrapper mỏng. Hãy cố gắng hiểu những gì nó thực sự làm, không chỉ là các loại thành viên riêng của nó. Có một số mã khá tinh tế ở đó.

  1. Có an toàn để không bảo vệ thông báo bằng mutex cho condition_variable_any hoặc condition_variable không?

Có. Trong thực tế, gọi notify_one() với mutex bị khóa sẽ khiến chủ đề chờ đợi thức dậy, cố gắng khóa mutex, tìm thấy nó vẫn bị khóa bởi chuỗi thông báo và quay trở lại trạng thái ngủ cho đến khi phát hành mutex.

Nếu bạn gọi notify_one() mà không có khóa mutex thì chủ đề đánh thức có thể chạy ngay lập tức.

2 Tại sao việc triển khai điều kiện_variable_any sử dụng thêm mutex?

condition_variable_any có thể được sử dụng với bất kỳ có thể khóa loại, không chỉ std:mutex, nhưng trong nội bộ một trong libstdC++ sử dụng một condition_variable, mà chỉ có thể được sử dụng với std::mutex, vì vậy nó có một đối tượng bên trong std::mutex quá.

Vì vậy, condition_variable_any hoạt động với hai mutex, thiết bị bên ngoài được cung cấp bởi người dùng và thiết bị bên ngoài được cung cấp bởi triển khai.

3 Tại sao thực hiện điều kiện_variable_any :: notify_one và condition_variable :: notify_one khác? Có thể condition_variable :: notify_one yêu cầu bảo vệ bằng tay nhưng condition_variable_any :: notify_one không? Có lỗi libstdC++ không?

Không, nó không phải là lỗi.

Tiêu chuẩn yêu cầu gọi số wait(mx) phải mở khóa nguyên bản mx và ngủ. libstdC++ sử dụng mutex nội bộ để cung cấp bảo đảm nguyên tử đó. Các mutex nội bộ phải được khóa để tránh thông báo bị bỏ lỡ nếu chủ đề khác chỉ là về để chờ đợi trên condition_variable_any.

+0

Cảm ơn! Điều đó đã giúp tôi rất nhiều. Espeically trả lời cho 2 và 3 - điểm tốt về atomicity, và bên trong thứ mà sử dụng mutex anyway. Nhân tiện, bạn có thể thêm liên kết vào pthread_cond_wait http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_cond_wait.html để cho thấy rằng triển khai phổ biến nhất chỉ hoạt động với mutex riêng của nó trong nội bộ. Bạn có thể vui lòng làm rõ chính xác những gì bạn có nghĩa là trên "có một số mã khá tinh tế ở đó." – qble

+0

Về 1. - có thể không bị khóa condition_variable :: notify_one dẫn đến thông báo bị nhỡ? I E. thread # 1 làm công việc dưới mutex, gửi kết quả, mở khóa mutex, [trong khi đó] thread # 2 khóa mutex nhưng chưa gọi là wait, [whilewhile] thread # 1 gọi notify_one, [whilewhile] thread # 2 calls wait - notification bị mất. – qble

+0

Đó không phải là thông báo bị mất, đó là chủ đề chờ đợi không kiểm tra biến vị ngữ điều kiện trước khi chờ. Khóa mutex không giúp được tình huống đó, thông báo vẫn có thể đến trước khi chuỗi chờ đợi khóa tiếng mutex. Bạn ** phải ** kiểm tra biến vị ngữ liên quan khi đợi trên biến điều kiện –

2

(1) Tôi không thấy bất kỳ lý do nào báo hiệu biến điều kiện phải được bảo vệ bởi một mutex, từ một điểm ẩn cuộc đua dữ liệu. Rõ ràng, bạn có khả năng nhận được thông báo thừa hoặc mất thông báo, nhưng nếu đây là điều kiện lỗi có thể chấp nhận hoặc có thể phục hồi cho chương trình của bạn, tôi không tin có bất kỳ điều gì trong tiêu chuẩn sẽ làm cho nó bất hợp pháp. Các tiêu chuẩn, tất nhiên, sẽ không bảo vệ bạn chống lại điều kiện chủng tộc; đó là trách nhiệm của người lập trình để đảm bảo rằng các điều kiện chủng tộc là lành tính. (Và, tất nhiên, điều quan trọng là lập trình viên không đặt bất kỳ "cuộc đua dữ liệu" nào được xác định rất cụ thể trong tiêu chuẩn nhưng không áp dụng trực tiếp cho nguyên tắc đồng bộ hóa hoặc hành vi không xác định được triệu tập.)

(2) Tôi không thể trả lời một câu hỏi như thế này về việc triển khai nội bộ của một cơ sở thư viện chuẩn. Đó là, tất nhiên, trách nhiệm của nhà cung cấp để cung cấp các cơ sở thư viện hoạt động chính xác và đáp ứng các đặc điểm kỹ thuật. Việc triển khai thư viện này có thể có một số trạng thái nội bộ yêu cầu loại trừ lẫn nhau để tránh tham nhũng hoặc nó có thể thực hiện khóa để tránh bị mất hoặc thừa thông báo. (Chỉ vì chương trình của bạn có thể tha thứ cho họ, không có nghĩa là người dùng tùy ý của thư viện có thể, và nói chung tôi mong đợi họ không thể.) Nó chỉ là suy đoán về phần tôi đang bảo vệ với mutex này.

(3) condition_variable_any được thiết kế để hoạt động trên bất kỳ đối tượng giống như khóa nào, trong khi condition_variable được thiết kế đặc biệt để hoạt động với unique_lock<mutex>.Sau này có thể dễ triển khai và/hoặc có hiệu suất cao hơn so với trước, vì nó biết cụ thể loại nào nó đang hoạt động và những gì chúng yêu cầu (cho dù chúng tầm thường, cho dù chúng có phù hợp với một dòng bộ nhớ cache hay không) tập hợp các syscalls của nền tảng cụ thể, hàng rào hoặc sự kết hợp bộ nhớ cache đảm bảo chúng ngụ ý, vv), trong khi trước đây cung cấp một cơ sở chung để hoạt động trên các đối tượng khóa-lock mà không bị ràng buộc cụ thể với các ràng buộc của std::mutex hoặc std::unique_lock<>.

+0

Có lẽ tôi nên thêm vào Q: triển khai điều kiện_variable_any chỉ là trình bao bọc mỏng xung quanh điều kiện_variable + mutex. – qble

+0

"Chỉ vì chương trình của bạn có thể tha thứ cho họ, không có nghĩa là người dùng tùy ý của thư viện có thể, và nói chung tôi không thể" - tôi nghĩ bạn không hiểu được - nếu tôi không bảo vệ thông báo của mình bởi mutex bởi bản thân tôi - có thể bị mất thông báo, nhưng nếu tôi bảo vệ nó - họ sẽ là không thể. Việc triển khai không nên quan tâm đến các thông báo bị mất khi người dùng không bảo vệ notify_one bằng mutex. – qble

+0

@qble: Một lần nữa, tôi không thể thực sự bình luận về những gì một nhà cung cấp thư viện chuẩn chọn để thực hiện việc thực hiện tiêu chuẩn. Chúng được phép sử dụng bất kỳ ma thuật trình biên dịch nào để nó hoạt động (bao gồm cả hành vi không xác định, nếu chúng xảy ra để biết rằng nền tảng của chúng định nghĩa nó) và chúng có thể tận dụng kiến ​​thức rất cụ thể. Có thể là họ biết rằng std :: lock_guard hoạt động đủ cho chúng, nhưng các loại được liên kết với thông số mẫu có thể không. Bạn có thể tìm thấy manh mối trong việc thực hiện wait(). –

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