Điều gì ngăn chặn một điều kiện cuộc đua trên một khóa?


24

Tôi hiểu những điều cơ bản về chủng tộc dữ liệu là gì và cách khóa / mutexes / semaphores giúp ngăn chặn chúng. Nhưng điều gì xảy ra nếu bạn có "điều kiện cuộc đua" trên khóa? Ví dụ, hai luồng khác nhau, có thể trong cùng một ứng dụng, nhưng chạy trên các bộ xử lý khác nhau, cố gắng thu được khóa cùng một lúc .

Chuyện gì xảy ra sau đó? Làm gì để ngăn chặn điều đó? Là nó không thể, hoặc chỉ đơn giản là không thể? Hay đó là một điều kiện cuộc đua thực sự đang chờ để xảy ra?


Câu hỏi này đã được hỏi trước đó trên SO: stackoverflow.com/questions/980521/iêu
Doc Brown

và một câu hỏi liên quan ở đây trên P.SE: programmers.stackexchange.com/questions/228827/...
ratchet quái

Bạn có được một khóa để có được khóa;) (nói cách khác, nếu khóa của bạn có điều kiện cuộc đua, thì nó không được triển khai chính xác - một khóa được xác định khá nhiều là một cấu trúc thực hiện loại trừ lẫn nhau)
Tangrs

Bạn đã bỏ lỡ một điểm quan trọng trong cách hoạt động của ổ khóa. Chúng được xây dựng sao cho không thể có một cuộc đua trên khóa, nếu không chúng hoàn toàn vô dụng.
Zane

Câu trả lời:


36

Là nó không thể, hoặc chỉ đơn giản là không thể?

Không thể nào. Nó có thể được thực hiện theo nhiều cách khác nhau, ví dụ, thông qua So sánh và trao đổi trong đó phần cứng đảm bảo thực hiện tuần tự. Nó có thể hơi phức tạp khi có nhiều lõi hoặc thậm chí nhiều ổ cắm và cần một giao thức phức tạp giữa các lõi, nhưng tất cả đều được quan tâm.


3
Cảm ơn ... Chúa ơi ... nó được xử lý trong phần cứng ... (hoặc ít nhất là một mức thấp hơn chúng ta chạm vào.)
corsiKa

2
@gdhoward Tôi không thể tin được ... câu trả lời này chỉ mất chưa đầy 5 phút và đây là câu trả lời cao thứ ba trong số bốn trăm câu trả lời của tôi (chủ yếu là SO). Và cũng có thể là ngắn nhất.
maaartinus

1
@maaartinus - Đôi khi ngắn và ngọt ngào.
Bobson

17

Nghiên cứu khái niệm về hoạt động "Thử và Đặt" nguyên tử.

Về cơ bản, hoạt động không thể được phân chia - hai điều không thể thực hiện cùng một lúc chính xác. Nó sẽ kiểm tra một giá trị, đặt nó nếu nó rõ ràng và trả về giá trị như khi thử nghiệm. Trong thao tác khóa, kết quả sẽ luôn là "khóa == TRUE" sau khi kiểm tra và thiết lập, sự khác biệt duy nhất là nó được đặt hay không khi bắt đầu.

Ở cấp độ mã vi mô trong bộ xử lý lõi đơn, đây là một hướng dẫn không thể tách rời và rất dễ thực hiện. Với bộ xử lý đa lõi và đa lõi, nó trở nên khó hơn, nhưng khi lập trình viên, chúng ta không cần phải lo lắng về nó, vì nó được thiết kế để hoạt động bởi những kẻ thực sự thông minh làm silicon. Về cơ bản, họ làm điều tương tự - thực hiện một hướng dẫn nguyên tử rằng một phiên bản thử nghiệm và thiết lập lạ mắt


2
Về cơ bản, nếu phần cứng không thực sự tuần tự ở một mức độ nào đó, nó sẽ có một cơ chế cho phép nó phá vỡ các mối quan hệ có thể xảy ra.
Bill Michell

@BillMichell, tôi nên nghĩ về điều đó. Thật ra, tôi đã làm; Tôi chỉ không biết liệu giả định của mình có đúng không.
Gavin Howard

2

Chỉ cần đặt mã để vào phần quan trọng được thiết kế đặc biệt để điều kiện cuộc đua sẽ không vi phạm loại trừ lẫn nhau.

Hầu hết các vòng lặp so sánh và thiết lập nguyên tử được sử dụng để thực thi ở cấp độ phần cứng

while(!CompareAndSet(&lock, false, true));//busy loop won't continue until THIS thread has set the lock to true
//critical section
CompareAndSet(&lock, true, false);

Trong trường hợp đó, có các giải pháp phần mềm được nghiên cứu kỹ lưỡng để cho phép loại trừ lẫn nhau.


1

Không thể có hai (hoặc nhiều) chủ đề có được khóa cùng một lúc. Ví dụ, có một số loại phương thức đồng bộ hóa:

Chờ đợi tích cực - khóa quay

Mã giả:

1. while ( xchg(lock, 1) == 1); - entry protocole

XCHG là một ví dụ về hoạt động nguyên tử (tồn tại trên kiến ​​trúc x86) trước tiên đặt giá trị mới cho biến "khóa" và sau đó trả về giá trị cũ. Nguyên tử có nghĩa là nó không thể bị gián đoạn - trong ví dụ trên giữa việc đặt giá trị mới và trả lại giá trị cũ. Nguyên tử - kết quả xác định không có vấn đề gì.

2. Your code
3. lock = 0; - exit protocol

Khi khóa bằng 0, một luồng khác có thể vào phần quan trọng - trong khi vòng lặp kết thúc.

Đình chỉ chủ đề - ví dụ đếm semaphore

Tồn tại hai hoạt động nguyên tử .Wait().Signal()và chúng tôi có số nguyên biến cho phép gọi nó int currentValue.

Wait():
if (currentValue > 0) currentValue -= 1;
else suspend current thread;

Signal():
If there exists thread suspended by semaphore wake up one of them
Else currentValue += 1;

Bây giờ giải quyết vấn đề phần quan trọng là thực sự dễ dàng:

Mã giả:

mySemaphore.Wait();
do some operations - critical section
mySemaphore.Signal();

Thông thường API luồng lập trình của bạn sẽ cung cấp cho bạn khả năng chỉ định các luồng đồng thời tối đa trong phần quan trọng semaphore. Rõ ràng có nhiều loại đồng bộ hóa hơn trong các hệ thống đa luồng (mutex, màn hình, semaphore nhị phân, v.v.) nhưng chúng dựa trên các ý tưởng trên. Người ta có thể lập luận rằng các phương thức sử dụng tạm dừng luồng nên được ưu tiên chờ đợi hoạt động (vì vậy cpu không bị lãng phí) - không phải lúc nào cũng là sự thật. Khi luồng đang bị treo - hoạt động đắt tiền được gọi là chuyển đổi ngữ cảnh diễn ra. Tuy nhiên, thật hợp lý khi thời gian chờ là ngắn (số luồng ~ số lõi).

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.