Gọi pthread_cond_signal mà không khóa mutex


82

Tôi đọc ở đâu đó rằng chúng ta nên khóa mutex trước khi gọi pthread_cond_signal và mở khóa mutext sau khi gọi nó:

Quy trình pthread_cond_signal () được sử dụng để báo hiệu (hoặc đánh thức) một luồng khác đang chờ biến điều kiện. Nó sẽ được gọi sau khi khóa mutex và phải mở khóa mutex để quy trình pthread_cond_wait () hoàn tất.

Câu hỏi của tôi là: phải không OK để gọi pthread_cond_signal hoặc pthread_cond_broadcast phương pháp mà không cần khóa mutex?

Câu trả lời:


140

Nếu bạn không khóa mutex trong đường dẫn thay đổi điều kiện và tín hiệu, bạn có thể mất đánh thức. Hãy xem xét cặp quy trình này:

Quy trình A:

pthread_mutex_lock(&mutex);
while (condition == FALSE)
    pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

Quy trình B (không chính xác):

condition = TRUE;
pthread_cond_signal(&cond);

Sau đó, hãy xem xét sự đan xen có thể có của các hướng dẫn này, conditionbắt đầu như FALSE:

Process A                             Process B

pthread_mutex_lock(&mutex);
while (condition == FALSE)

                                      condition = TRUE;
                                      pthread_cond_signal(&cond);

pthread_cond_wait(&cond, &mutex);

Hiện conditiontại là TRUE, nhưng Quy trình A bị kẹt khi chờ biến điều kiện - nó đã bỏ lỡ tín hiệu đánh thức. Nếu chúng tôi thay đổi Quy trình B để khóa mutex:

Quy trình B (đúng):

pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

... thì điều trên không thể xảy ra; thức dậy sẽ không bao giờ bị bỏ lỡ.

(Lưu ý rằng bạn thực sự có thể di chuyển pthread_cond_signal()chính nó sau pthread_mutex_unlock(), nhưng điều này có thể dẫn đến việc lập lịch các luồng kém tối ưu hơn và bạn nhất thiết phải khóa mutex đã có trong đường dẫn mã này do chính điều kiện thay đổi).


3
@Nemo: Có, trong đường dẫn "đúng", bạn pthread_signal_cond() thể di chuyển sau khi mở khóa mutex, mặc dù có lẽ tốt hơn là không nên. Có lẽ đúng hơn khi nói rằng tại thời điểm bạn đang gọi pthread_signal_cond(), bạn sẽ cần phải khóa mutex để tự sửa đổi điều kiện.
caf

@Nemo: Vâng, mã bị hỏng, đó là lý do tại sao nó bị đánh dấu là "không chính xác" - theo kinh nghiệm của tôi rằng những người đang hỏi câu hỏi này thường xem xét việc bỏ khóa hoàn toàn trên đường dẫn tín hiệu. Có lẽ kinh nghiệm của bạn khác nhau.
caf

8
Đối với những gì nó đáng giá, có vẻ như câu hỏi về việc nên mở khóa trước hay sau khi có tín hiệu thực sự không nhỏ để trả lời. Việc mở khóa sau khi đảm bảo rằng một luồng có mức độ ưu tiên thấp hơn sẽ không thể đánh cắp sự kiện từ luồng có mức độ ưu tiên cao hơn, nhưng nếu bạn không sử dụng mức độ ưu tiên, việc mở khóa trước khi có tín hiệu sẽ thực sự giảm số lượng cuộc gọi hệ thống / chuyển đổi ngữ cảnh và cải thiện hiệu suất tổng thể.
R .. GitHub DỪNG TRỢ GIÚP LÚC NỮA, 29/09

1
@R .. Bạn bị ngược. Mở khóa trước khi có tín hiệu làm tăng số lượng cuộc gọi của hệ thống và giảm hiệu suất tổng thể. Nếu bạn báo hiệu trước khi mở khóa, việc triển khai biết rằng tín hiệu không thể đánh thức một luồng (vì bất kỳ luồng nào bị chặn trên cv cần mutex để thực hiện tiến trình chuyển tiếp), cho phép nó sử dụng đường dẫn nhanh. Nếu bạn ra hiệu sau khi mở khóa, cả tín hiệu mở khóa và tín hiệu đều có thể đánh thức các luồng, có nghĩa là cả hai đều là hoạt động tốn kém.
David Schwartz

1
@Alceste_: Xem văn bản này trong POSIX : "Khi một chuỗi chờ một biến điều kiện, đã chỉ định một mutex cụ thể cho pthread_cond_timedwait()hoặc pthread_cond_wait()hoạt động, một liên kết động được hình thành giữa biến mutex và điều kiện đó vẫn có hiệu lực miễn là tại ít nhất một thread bị chặn trên biến điều kiện trong thời gian này, ảnh hưởng của một nỗ lực bởi bất kỳ thread đợi trên biến tình trạng sử dụng một mutex khác nhau được xác định.. "
caf

48

Theo hướng dẫn này:

Các pthread_cond_broadcast()hoặc các pthread_cond_signal()hàm có thể được gọi bởi một luồng cho dù nó hiện đang sở hữu mutex mà các luồng đang gọi pthread_cond_wait() hoặc pthread_cond_timedwait()có liên kết với biến điều kiện trong thời gian chờ của chúng hay không; tuy nhiên, nếu hành vi lập lịch dự đoán được yêu cầu, thì mutex đó sẽ bị khóa bởi luồng gọi pthread_cond_broadcast()hoặc pthread_cond_signal().

Ý nghĩa của câu lệnh hành vi lập lịch có thể dự đoán được đã được Dave Butenhof (tác giả của Lập trình với các luồng POSIX ) giải thích trên comp.programming.threads và có sẵn tại đây .


6
+1 để liên kết thư từ Dave Butenhof. Tôi luôn tự hỏi về vấn đề này, bây giờ tôi biết ... Hôm nay đã học được một điều quan trọng. Cảm ơn.
Nils Pipenbrinck,

Nếu hành vi lập lịch có thể dự đoán được là bắt buộc, thì hãy đặt các câu lệnh trong một luồng theo thứ tự bạn muốn hoặc sử dụng mức độ ưu tiên của luồng.
Kaz

7

caf, trong mã mẫu của bạn, Quy trình B sửa đổi conditionmà không khóa mutex trước. Nếu Quy trình B chỉ đơn giản là khóa mutex trong quá trình sửa đổi đó, và sau đó vẫn mở khóa mutex trước khi gọi pthread_cond_signal, sẽ không có vấn đề gì --- tôi có đúng về điều đó không?

Tôi tin trực giác rằng vị trí của caf là đúng: gọi điện pthread_cond_signalmà không sở hữu khóa mutex là một Ý tưởng Xấu. Nhưng ví dụ của caf thực sự không phải là bằng chứng ủng hộ quan điểm này; nó chỉ đơn giản là bằng chứng ủng hộ quan điểm yếu hơn nhiều (thực tế là hiển nhiên) rằng việc sửa đổi trạng thái được chia sẻ được bảo vệ bởi mutex là một Ý tưởng Xấu, trừ khi bạn đã khóa mutex đó trước.

Bất cứ ai có thể cung cấp một số mã mẫu trong đó lệnh gọi pthread_cond_signaltheo sau pthread_mutex_unlockmang lại hành vi đúng, nhưng gọi pthread_mutex_unlocktheo sau là pthread_cond_signalhành vi không chính xác?


3
Trên thực tế, tôi nghĩ câu hỏi của mình trùng lặp với câu hỏi này và câu trả lời là "Không sao cả, bạn hoàn toàn có thể gọi pthread_cond_signal mà không cần sở hữu khóa mutex. Không có vấn đề về độ chính xác. Nhưng trong các cách triển khai thông thường, bạn sẽ bỏ lỡ một cách tối ưu hóa thông minh sâu bên trong pthreads, vì vậy bạn nên gọi pthread_cond_signal trong khi vẫn giữ khóa. "
Quuxplusone

Tôi đã đưa ra nhận định này trong đoạn cuối cùng của câu trả lời của mình.
caf

Bạn có một kịch bản tốt ở đây: stackoverflow.com/a/6419626/1284631 Lưu ý rằng nó không khẳng định hành vi là không chính xác, nó chỉ đưa ra một trường hợp mà hành vi có thể không như mong đợi.
dùng1284631

Có thể tạo mã mẫu trong đó việc gọi pthread_cond_signalsau pthread_mutex_unlockcó thể dẫn đến việc đánh thức bị mất vì tín hiệu bị bắt là chuỗi "sai", một chuỗi bị chặn sau khi thấy sự thay đổi trong vị từ. Đây chỉ là một vấn đề nếu cùng một biến điều kiện có thể được sử dụng cho nhiều vị từ và bạn không sử dụng pthread_cond_broadcast, đây là một mẫu hiếm và dễ vỡ.
David Schwartz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.