Biến có điều kiện so với Semaphore


Câu trả lời:


207

Các ổ khóa được sử dụng để loại trừ lẫn nhau. Khi bạn muốn đảm bảo rằng một đoạn mã là nguyên tử, hãy đặt một chiếc khóa xung quanh nó. Về mặt lý thuyết, bạn có thể sử dụng một semaphore nhị phân để làm điều này, nhưng đó là một trường hợp đặc biệt.

Semaphores và các biến điều kiện xây dựng trên cơ sở loại trừ lẫn nhau được cung cấp bởi các khóa và được sử dụng để cung cấp quyền truy cập đồng bộ vào các tài nguyên được chia sẻ. Chúng có thể được sử dụng cho các mục đích tương tự.

Một biến điều kiện thường được sử dụng để tránh việc chờ đợi bận rộn (lặp đi lặp lại trong khi kiểm tra một điều kiện) trong khi chờ tài nguyên sẵn có. Ví dụ: nếu bạn có một chuỗi (hoặc nhiều chuỗi) không thể tiếp tục trở đi cho đến khi hàng đợi trống, thì cách tiếp cận chờ bận sẽ là chỉ thực hiện một số việc như:

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

Vấn đề với điều này là bạn đang lãng phí thời gian của bộ xử lý bằng cách để luồng này liên tục kiểm tra điều kiện. Thay vào đó, tại sao không có một biến đồng bộ hóa có thể được báo hiệu để báo cho luồng biết rằng tài nguyên có sẵn?

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

Có lẽ, bạn sẽ có một luồng ở đâu đó khác đang kéo mọi thứ ra khỏi hàng đợi. Khi hàng đợi trống, nó có thể gọi syncVar.signal()để đánh thức một chuỗi ngẫu nhiên đang ở chế độ ngủ syncVar.wait()(hoặc thường cũng có một signalAll()hoặc broadcast()phương thức để đánh thức tất cả các chuỗi đang chờ).

Tôi thường sử dụng các biến đồng bộ hóa như thế này khi tôi có một hoặc nhiều luồng đang chờ trên một điều kiện cụ thể (ví dụ: hàng đợi trống).

Semaphores có thể được sử dụng tương tự, nhưng tôi nghĩ rằng chúng được sử dụng tốt hơn khi bạn có một tài nguyên được chia sẻ có thể có sẵn và không có sẵn dựa trên một số số nguyên của những thứ có sẵn. Semaphores phù hợp với các tình huống của nhà sản xuất / người tiêu dùng khi nhà sản xuất đang phân bổ nguồn lực và người tiêu dùng đang tiêu thụ chúng.

Hãy nghĩ xem bạn có máy bán nước ngọt tự động không. Chỉ có một máy làm nước ngọt và đó là tài nguyên dùng chung. Bạn có một chủ đề là nhà cung cấp (nhà sản xuất) chịu trách nhiệm giữ máy và N luồng là người mua (người tiêu dùng) muốn lấy nước ngọt ra khỏi máy. Số lượng nước ngọt trong máy là giá trị số nguyên sẽ điều khiển semaphore của chúng ta.

Mọi chủ đề của người mua (người tiêu dùng) đến với máy làm nước ngọt gọi down()phương pháp semaphore để lấy một cốc nước ngọt. Thao tác này sẽ lấy một lon nước ngọt từ máy và giảm số lượng nước ngọt có sẵn đi 1. Nếu có nước ngọt, mã sẽ tiếp tục chạy qua down()câu lệnh mà không có vấn đề gì. Nếu không có nước ngọt có sẵn, chuỗi sẽ ngủ ở đây để chờ được thông báo về thời điểm có nước ngọt trở lại (khi có thêm nước ngọt trong máy).

Chủ đề của nhà cung cấp (nhà sản xuất) về cơ bản sẽ đợi máy làm soda trống. Nhà cung cấp sẽ nhận được thông báo khi nước ngọt cuối cùng được lấy ra khỏi máy (và một hoặc nhiều người tiêu dùng đang chờ lấy nước ngọt ra). Nhà cung cấp sẽ trang bị lại máy làm nước ngọt bằng up()phương pháp semaphore , số lượng nước ngọt có sẵn sẽ được tăng lên mỗi lần và do đó các chủ đề của người tiêu dùng đang chờ đợi sẽ được thông báo rằng có nhiều nước ngọt hơn.

Các phương thức wait()signal()phương thức của một biến đồng bộ hóa có xu hướng bị ẩn bên trong down()và các up()hoạt động của semaphore.

Chắc chắn có sự trùng lặp giữa hai sự lựa chọn. Có nhiều trường hợp mà một semaphore hoặc một biến điều kiện (hoặc tập hợp các biến điều kiện) đều có thể phục vụ mục đích của bạn. Cả semaphores và biến điều kiện đều được liên kết với một đối tượng khóa mà chúng sử dụng để duy trì loại trừ lẫn nhau, nhưng sau đó chúng cung cấp chức năng bổ sung trên đầu khóa để đồng bộ hóa việc thực thi luồng. Phần lớn tùy thuộc vào bạn để tìm ra cách nào phù hợp nhất với tình huống của bạn.

Đó không nhất thiết là mô tả kỹ thuật nhất, nhưng đó là cách nó có ý nghĩa trong đầu tôi.


9
Câu trả lời tuyệt vời, tôi muốn thêm từ các câu trả lời khác: Semaphore được sử dụng để kiểm soát số lượng các luồng thực thi. Sẽ có một bộ tài nguyên cố định. Số lượng tài nguyên sẽ giảm đi mỗi khi một luồng sở hữu giống nhau. Khi số lượng semaphore bằng 0 thì không có luồng nào khác được phép lấy tài nguyên. Các chủ đề bị chặn cho đến khi các chủ đề khác sở hữu tài nguyên giải phóng. Tóm lại, sự khác biệt chính là có bao nhiêu luồng được phép lấy tài nguyên cùng một lúc? Mutex --its MỘT. Semaphore - DEFINED_COUNT của nó, (nhiều như số lượng semaphore)
berkay

10
Chỉ để giải thích tại sao lại có vòng lặp while này thay vì if: một cái gì đó được gọi là đánh thức spurios . Trích dẫn bài viết này wikipedia : "Một trong những lý do cho điều này là một wakeup giả mạo, đó là một chủ đề có thể được đánh thức từ trạng thái chờ đợi của nó mặc dù không có sợi báo hiệu sự biến điều kiện"
Vladislavs Burakovs

3
@VladislavsBurakovs Điểm tốt! Tôi nghĩ rằng nó cũng hữu ích cho trường hợp một chương trình phát sóng đánh thức nhiều luồng hơn là có sẵn tài nguyên (ví dụ: chương trình phát sóng đánh thức 3 luồng, nhưng chỉ có 2 mục trong hàng đợi).
Brent viết mã

Tôi ước tôi sẽ tán thành câu trả lời của bạn cho đến khi hàng đợi đầy;) Câu trả lời hoàn hảo. Mã này có thể giúp tìm ra semaphores csc.villanova.edu/~mdamian/threads/PC.htm
Mohamad-Jaafar NEHME

3
@VladislavsBurakovs Để làm rõ một chút, lý do mà điều kiện vẫn có thể sai đối với một chuỗi vừa mới thức dậy (dẫn đến việc đánh thức giả) là có thể đã có một chuyển đổi ngữ cảnh trước khi chuỗi có cơ hội kiểm tra điều kiện một lần nữa, trong đó một số chuỗi được lập lịch khác đã thực hiện điều kiện đó sai. Đây là một lý do mà tôi biết cho một lần đánh thức giả, không biết có nhiều hơn không.
Tối đa

52

Hãy tiết lộ những gì dưới mui xe.

Biến có điều kiện về cơ bản là một hàng đợi , hỗ trợ các hoạt động chặn-đợi và đánh thức, tức là bạn có thể đặt một luồng vào hàng đợi và đặt trạng thái của nó thành BLOCK, và lấy một luồng ra khỏi nó và đặt trạng thái của nó thành SN SÀNG.

Lưu ý rằng để sử dụng một biến có điều kiện, cần có hai phần tử khác:

  • một điều kiện (thường được thực hiện bằng cách kiểm tra cờ hoặc bộ đếm)
  • một mutex bảo vệ tình trạng

Giao thức sau đó trở thành,

  1. có được mutex
  2. Kiểm tra tình trạng
  3. chặn và giải phóng mutex nếu điều kiện là đúng, nếu không, hãy phát hành mutex

Semaphore thực chất là một bộ đếm + một mutex + một hàng đợi. Và nó có thể được sử dụng như nó vốn có mà không cần phụ thuộc vào bên ngoài. Bạn có thể sử dụng nó dưới dạng mutex hoặc như một biến có điều kiện.

Do đó, semaphore có thể được coi là một cấu trúc phức tạp hơn so với biến có điều kiện, trong khi cấu trúc thứ hai nhẹ và linh hoạt hơn.


mutex có thể được xem như một biến điều kiện, điều kiện là có được giữ hay không.
宏杰 李

18

Semaphores có thể được sử dụng để triển khai quyền truy cập độc quyền vào các biến, tuy nhiên chúng được sử dụng để đồng bộ hóa. Mặt khác, Mutexes có ngữ nghĩa liên quan chặt chẽ đến việc loại trừ lẫn nhau: chỉ quá trình đã khóa tài nguyên mới được phép mở khóa.

Rất tiếc, bạn không thể triển khai đồng bộ hóa với mutexes, đó là lý do tại sao chúng tôi có các biến điều kiện. Cũng lưu ý rằng với các biến điều kiện, bạn có thể mở khóa tất cả các chuỗi đang chờ trong cùng một thời điểm bằng cách sử dụng mở khóa phát sóng. Điều này không thể được thực hiện với semaphores.


9

semaphore và biến điều kiện rất giống nhau và được sử dụng hầu hết cho các mục đích giống nhau. Tuy nhiên, có những khác biệt nhỏ có thể làm cho một cái tốt hơn. Ví dụ, để thực hiện đồng bộ hóa rào cản, bạn sẽ không thể sử dụng semaphore, nhưng một biến điều kiện là lý tưởng.

Đồng bộ hóa rào cản là khi bạn muốn tất cả các luồng của mình đợi cho đến khi mọi người đã đến một phần nhất định trong chức năng luồng. điều này có thể được thực hiện bằng cách có một biến tĩnh mà ban đầu là giá trị của tổng số luồng giảm theo từng luồng khi nó đạt đến rào cản đó. điều này có nghĩa là chúng ta muốn mỗi luồng ở chế độ ngủ cho đến khi luồng cuối cùng đến. semaphore sẽ làm ngược lại! với một semaphore, mỗi luồng sẽ tiếp tục chạy và luồng cuối cùng (sẽ đặt giá trị semaphore thành 0) sẽ chuyển sang chế độ ngủ.

Mặt khác, một biến điều kiện là lý tưởng. khi mỗi luồng đi đến rào cản, chúng tôi kiểm tra xem bộ đếm tĩnh của chúng tôi có bằng không. nếu không, chúng tôi đặt luồng ở trạng thái ngủ với hàm chờ biến điều kiện. khi luồng cuối cùng đến hàng rào, giá trị bộ đếm sẽ giảm xuống 0 và luồng cuối cùng này sẽ gọi hàm tín hiệu biến điều kiện sẽ đánh thức tất cả các luồng khác!


1

Tôi gửi các biến điều kiện theo đồng bộ hóa màn hình. Tôi thường thấy semaphores và màn hình là hai kiểu đồng bộ hóa khác nhau. Có sự khác biệt giữa cả hai về lượng dữ liệu trạng thái vốn có và cách bạn muốn lập mô hình mã - nhưng thực sự không có bất kỳ vấn đề nào có thể được giải quyết bởi cái này mà không phải cái kia.

Tôi có xu hướng viết mã theo hình thức giám sát; trong hầu hết các ngôn ngữ tôi làm việc liên quan đến mutexes, biến điều kiện và một số biến trạng thái hỗ trợ. Nhưng semaphores cũng sẽ làm công việc.


2
Đây sẽ là câu trả lời tốt hơn nếu bạn giải thích "biểu mẫu giám sát" là gì.
Steven Lu

0

Các mutexconditional variablesđược kế thừa từ semaphore.

  • Đối với mutex, semaphoresử dụng hai trạng thái: 0, 1
  • Đối với condition variablesbộ semaphore đếm sử dụng.

Chúng giống như đường cú pháp


Trong thư viện C ++ std, chúng là tất cả các đối tượng cấp huyện, tất cả đều được triển khai bằng cách sử dụng các API cụ thể của nền tảng. Chắc chắn một semaphore sẽ bỏ chặn số lần được báo hiệu, biến điều kiện có thể được báo hiệu nhiều lần nhưng chỉ bỏ chặn một lần. Đây là lý do tại sao wair lấy mutex làm tham số.
doron
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.