Khi nào chúng ta nên sử dụng mutex và khi nào chúng ta nên sử dụng semaphore?
Khi nào chúng ta nên sử dụng mutex và khi nào chúng ta nên sử dụng semaphore?
Câu trả lời:
Đây là cách tôi nhớ khi sử dụng cái gì -
Semaphore: Sử dụng một semaphore khi bạn (chủ đề) muốn ngủ cho đến khi một số chủ đề khác yêu cầu bạn thức dậy. Semaphore 'xuống' xảy ra trong một luồng (nhà sản xuất) và semaphore 'lên' (cho cùng một semaphore) xảy ra trong một luồng khác (người tiêu dùng), ví dụ: Trong bài toán nhà sản xuất-người tiêu dùng, nhà sản xuất muốn ngủ cho đến khi ít nhất một vùng đệm trống - chỉ luồng tiêu dùng có thể cho biết khi nào một khe đệm trống.
Mutex: Sử dụng mutex khi bạn (luồng) muốn thực thi mã không được thực thi bởi bất kỳ luồng nào khác cùng một lúc. Mutex 'xuống' xảy ra trong một chuỗi và mutex 'lên' phải xảy ra trong cùng một chuỗi sau này. Ví dụ: Nếu bạn đang xóa một nút khỏi danh sách được liên kết chung, bạn không muốn một luồng khác xen kẽ với các con trỏ trong khi bạn đang xóa nút. Khi bạn có được một mutex và đang bận xóa một nút, nếu một luồng khác cố gắng lấy cùng một mutex, nó sẽ được chuyển sang trạng thái ngủ cho đến khi bạn giải phóng mutex.
Spinlock: Sử dụng spinlock khi bạn thực sự muốn sử dụng mutex nhưng luồng của bạn không được phép ở chế độ ngủ. ví dụ: Một trình xử lý ngắt trong hạt nhân hệ điều hành không bao giờ được ngủ. Nếu nó xảy ra, hệ thống sẽ đóng băng / sập. Nếu bạn cần chèn một nút vào danh sách liên kết được chia sẻ toàn cầu từ trình xử lý ngắt, hãy có một spinlock - chèn nút - giải phóng spinlock.
Một mutex là một đối tượng loại trừ lẫn nhau, tương tự như semaphore nhưng nó chỉ cho phép một khóa tại một thời điểm và các hạn chế về quyền sở hữu của nó có thể nghiêm ngặt hơn semaphore.
Nó có thể được coi là tương đương với một semaphore đếm bình thường (với số đếm là một) và yêu cầu rằng nó chỉ có thể được giải phóng bởi cùng một luồng đã khóa nó (a) .
Mặt khác, một semaphore có số lượng tùy ý và có thể bị khóa bởi nhiều khóa cùng một lúc. Và nó có thể không có yêu cầu rằng nó được phát hành bởi cùng một chủ đề đã tuyên bố nó (nhưng, nếu không, bạn phải theo dõi cẩn thận xem ai hiện đang chịu trách nhiệm về nó, giống như bộ nhớ được cấp phát).
Vì vậy, nếu bạn có một số trường hợp của tài nguyên (ví dụ ba ổ băng), bạn có thể sử dụng semaphore với số lượng là 3. Lưu ý rằng điều này không cho bạn biết bạn có ổ băng nào, chỉ là bạn có một số nhất định.
Cũng với semaphores, một bộ khóa duy nhất có thể khóa nhiều bản sao của một tài nguyên, chẳng hạn như một bản sao từ băng sang băng. Nếu bạn có một tài nguyên (giả sử vị trí bộ nhớ mà bạn không muốn làm hỏng), mutex phù hợp hơn.
Các phép toán tương đương là:
Counting semaphore Mutual exclusion semaphore
-------------------------- --------------------------
Claim/decrease (P) Lock
Release/increase (V) Unlock
Ngoài ra: trong trường hợp bạn đã từng thắc mắc về những chữ cái kỳ lạ được sử dụng để yêu cầu và phát hành các semaphores, đó là bởi vì nhà phát minh là người Hà Lan. Probeer te verlagen có nghĩa là cố gắng và giảm trong khi verhogen có nghĩa là tăng lên.
(a) ... hoặc nó có thể được coi là một cái gì đó hoàn toàn khác biệt với semaphore, có thể an toàn hơn với cách sử dụng hầu như luôn khác nhau của chúng.
Điều rất quan trọng là phải hiểu rằng mutex không phải là semaphore với số đếm 1!
Đây là lý do tại sao có những thứ như semaphores nhị phân (thực sự là semaphores với số đếm 1).
Sự khác biệt giữa Mutex và Binary-Semaphore là nguyên tắc sở hữu:
Một mutex có được bởi một nhiệm vụ và do đó cũng phải được phát hành bởi cùng một nhiệm vụ. Điều này làm cho nó có thể khắc phục một số vấn đề với các semaphores nhị phân (Phát hành ngẫu nhiên, khóa chết đệ quy và đảo ngược ưu tiên).
Lưu ý: Tôi đã viết "làm cho nó có thể", nếu và cách những vấn đề này được khắc phục là tùy thuộc vào việc triển khai hệ điều hành.
Bởi vì mutex phải được phát hành bởi cùng một tác vụ, nó không tốt cho việc đồng bộ hóa các tác vụ. Nhưng nếu kết hợp với các biến điều kiện, bạn sẽ có được các khối xây dựng rất mạnh mẽ để xây dựng tất cả các loại nguyên thủy ipc.
Vì vậy, khuyến nghị của tôi là: nếu bạn đã triển khai sạch sẽ các mutexes và các biến điều kiện (như với pthreads POSIX), hãy sử dụng chúng.
Chỉ sử dụng các semaphores nếu chúng phù hợp chính xác với vấn đề bạn đang cố gắng giải quyết, không cố gắng tạo các bản gốc khác (ví dụ: rw-lock ra khỏi các semaphores, sử dụng mutex và các biến điều kiện cho những thứ này)
Có rất nhiều hiểu lầm về mutexes và semaphores. Lời giải thích tốt nhất mà tôi tìm thấy cho đến nay là trong bài viết 3 Phần này:
Mutex vs. Semaphores - Phần 1: Semaphores
Mutex vs. Semaphores - Phần 2: The Mutex
Mutex vs. Semaphores - Phần 3 (phần cuối): Vấn đề loại trừ lẫn nhau
Trong khi câu trả lời @opaxdiablo là hoàn toàn chính xác, tôi muốn chỉ ra rằng kịch bản sử dụng của cả hai thứ là khá khác nhau. Mutex được sử dụng để bảo vệ các phần mã chạy đồng thời, các semaphores được sử dụng cho một luồng để báo hiệu cho một luồng khác chạy.
/* Task 1 */
pthread_mutex_lock(mutex_thing);
// Safely use shared resource
pthread_mutex_unlock(mutex_thing);
/* Task 2 */
pthread_mutex_lock(mutex_thing);
// Safely use shared resource
pthread_mutex_unlock(mutex_thing); // unlock mutex
Kịch bản semaphore thì khác:
/* Task 1 - Producer */
sema_post(&sem); // Send the signal
/* Task 2 - Consumer */
sema_wait(&sem); // Wait for signal
Xem http://www.netrino.com/node/202 để được giải thích thêm
sema_wait
:-) Theo ý kiến của tôi, chúng đều là tài nguyên và thông báo được chuyển đến các luồng khác là một tác dụng phụ (rất quan trọng, hiệu quả) của sự bảo vệ.
You say that the usage pattern of semaphores is to notify threads
Một điểm về thông báo chủ đề. Bạn có thể gọi sem_post
một cách an toàn từ bộ xử lý tín hiệu ( pubs.opengroup.org/onlinepubs/009695399/functions/… ) nhưng không được khuyến khích gọi pthread_mutex_lock
và pthread_mutex_unlock
từ bộ xử lý tín hiệu ( manpages.ubuntu.com/manpages/lucid/man3/… )
Xem "Ví dụ về nhà vệ sinh" - http://pheatt.emporia.edu/courses/2010/cs557f10/hand07/Mutex%20vs_%20Semaphore.htm :
Mutex:
Là một chìa khóa của một nhà vệ sinh. Một người có thể có chìa khóa - chiếm nhà vệ sinh - vào thời điểm đó. Khi hoàn thành, người đó đưa (giải phóng) chìa khóa cho người tiếp theo trong hàng đợi.
Về mặt chính thức: "Mutex thường được sử dụng để tuần tự hóa quyền truy cập vào một phần của mã đăng nhập lại mà nhiều luồng không thể thực thi đồng thời. Một đối tượng mutex chỉ cho phép một luồng vào một phần được kiểm soát, buộc các luồng khác cố gắng giành quyền truy cập vào phần đó để đợi cho đến khi chủ đề đầu tiên thoát khỏi phần đó. " Tham khảo: Thư viện nhà phát triển Symbian
(Một mutex thực sự là một semaphore với giá trị 1.)
Semaphore:
Là số lượng chìa khóa nhà vệ sinh giống hệt nhau miễn phí. Ví dụ, giả sử chúng ta có bốn nhà vệ sinh với ổ khóa và chìa khóa giống hệt nhau. Số lượng semaphore - số lượng khóa - được đặt thành 4 lúc đầu (tất cả bốn nhà vệ sinh đều miễn phí), sau đó giá trị đếm sẽ giảm dần khi có người vào. Tức là nếu tất cả các nhà vệ sinh đều đầy. không còn khóa trống nào, số lượng semaphore là 0. Bây giờ, khi eq. một người rời khỏi nhà vệ sinh, semaphore được tăng lên 1 (một khóa miễn phí), và được trao cho người tiếp theo trong hàng đợi.
Chính thức: "Một semaphore hạn chế số lượng người dùng đồng thời của một tài nguyên được chia sẻ tối đa. Các luồng có thể yêu cầu quyền truy cập vào tài nguyên (giảm semaphore) và có thể báo hiệu rằng họ đã sử dụng xong tài nguyên (tăng semaphore). " Tham khảo: Thư viện nhà phát triển Symbian
Cố gắng không nghe có vẻ kỳ quái, nhưng không thể giúp bản thân mình.
Câu hỏi của bạn nên là sự khác biệt giữa mutex và semaphores là gì? Và để chính xác hơn câu hỏi nên là, 'mối quan hệ giữa mutex và semaphores là gì?'
(Tôi đã thêm câu hỏi đó nhưng tôi chắc chắn 100% rằng một số người kiểm duyệt quá nhiệt tình sẽ đóng nó là trùng lặp mà không hiểu sự khác biệt giữa sự khác biệt và mối quan hệ.)
Trong thuật ngữ đối tượng, chúng ta có thể quan sát thấy rằng:
1 Semaphore chứa mutex
2 Mutex không phải là semaphore và semaphore không phải là mutex.
Có một số semaphores sẽ hoạt động như thể chúng là mutex, được gọi là semaphores nhị phân, nhưng chúng thật kỳ lạ KHÔNG phải mutex.
Có một thành phần đặc biệt gọi là Signaling (posix sử dụng condition_variable cho tên đó), được yêu cầu để tạo ra Semaphore từ mutex. Hãy coi nó như một nguồn thông báo. Nếu hai hoặc nhiều chuỗi được đăng ký cho cùng một nguồn thông báo, thì bạn có thể gửi tin nhắn của họ tới MỘT hoặc TẤT CẢ, để đánh thức.
Có thể có một hoặc nhiều bộ đếm được liên kết với semaphores, được bảo vệ bởi mutex. Kịch bản đơn giản nhất cho semaphore, có một bộ đếm duy nhất có thể là 0 hoặc 1.
Đây là nơi mà sự nhầm lẫn tràn vào như mưa gió mùa.
Semaphore có bộ đếm có thể là 0 hoặc 1 KHÔNG phải là mutex.
Mutex có hai trạng thái (0,1) và một quyền sở hữu (tác vụ). Semaphore có một mutex, một số bộ đếm và một biến điều kiện.
Bây giờ, hãy sử dụng trí tưởng tượng của bạn, và mọi sự kết hợp giữa việc sử dụng bộ đếm và thời điểm phát tín hiệu có thể tạo ra một loại Semaphore.
Bộ đếm duy nhất có giá trị 0 hoặc 1 và báo hiệu khi giá trị đi đến 1 VÀ sau đó mở khóa một trong những người đang chờ tín hiệu == Binary semaphore
Bộ đếm đơn với giá trị từ 0 đến N và báo hiệu khi giá trị nhỏ hơn N và khóa / chờ khi giá trị là N == Đếm semaphore
Bộ đếm đơn với giá trị từ 0 đến N và báo hiệu khi giá trị chuyển đến N và khóa / đợi khi giá trị nhỏ hơn N == Barrier semaphore (nếu họ không gọi nó, thì họ nên làm như vậy).
Bây giờ đến câu hỏi của bạn, khi nào thì sử dụng cái gì. (HOẶC đúng hơn là phiên bản câu hỏi. 3 khi nào sử dụng mutex và khi nào sử dụng binary-semaphore, vì không có sự so sánh nào với non-binary-semaphore.) Sử dụng mutex khi 1. bạn muốn một hành vi tùy chỉnh, hành vi đó không được cung cấp bởi binary semaphore, chẳng hạn như khóa quay hoặc khóa nhanh hoặc khóa đệ quy. Bạn thường có thể tùy chỉnh mutexes với các thuộc tính, nhưng tùy chỉnh semaphore không gì khác ngoài việc viết semaphore mới. 2. bạn muốn bản gốc nhẹ HOẶC nhanh hơn
Sử dụng semaphores, khi những gì bạn muốn được nó cung cấp chính xác.
Nếu bạn không hiểu những gì đang được cung cấp bởi việc triển khai binary-semaphore, thì IMHO, hãy sử dụng mutex.
Và cuối cùng hãy đọc một cuốn sách thay vì chỉ dựa vào SO.
Tôi nghĩ rằng câu hỏi nên là sự khác biệt giữa mutex và semaphore nhị phân.
Mutex = Đây là cơ chế khóa quyền sở hữu, chỉ chủ đề có được khóa mới có thể mở khóa.
binary Semaphore = Nó là một cơ chế tín hiệu, bất kỳ luồng nào khác có mức độ ưu tiên cao hơn nếu muốn đều có thể báo hiệu và lấy khóa.
Mutex là để bảo vệ tài nguyên được chia sẻ.
Semaphore là gửi các chủ đề.
Mutex:
Hãy tưởng tượng rằng có một số vé để bán. Chúng ta có thể mô phỏng trường hợp nhiều người mua vé cùng lúc: mỗi người là một sợi dây mua vé. Rõ ràng là chúng ta cần sử dụng mutex để bảo vệ các vé vì nó là tài nguyên được chia sẻ.
Semaphore:
Hãy tưởng tượng rằng chúng ta cần thực hiện một phép tính như sau:
c = a + b;
Ngoài ra, chúng ta cần một hàm geta()
để tính toán a
, một hàm getb()
để tính toán b
và một hàm getc()
để thực hiện phép tính c = a + b
.
Rõ ràng, chúng tôi không thể làm c = a + b
trừ khi geta()
và getb()
đã được hoàn thành.
Nếu ba chức năng là ba luồng, chúng ta cần gửi ba luồng.
int a, b, c;
void geta()
{
a = calculatea();
semaphore_increase();
}
void getb()
{
b = calculateb();
semaphore_increase();
}
void getc()
{
semaphore_decrease();
semaphore_decrease();
c = a + b;
}
t1 = thread_create(geta);
t2 = thread_create(getb);
t3 = thread_create(getc);
thread_join(t3);
Với sự giúp đỡ của các semaphore, các mã trên có thể đảm bảo rằng t3
sẽ không làm công việc của mình cho đến khi t1
và t2
đã làm công việc của họ.
Nói một cách dễ hiểu, semaphore là làm cho các luồng thực thi như một thứ tự logic trong khi mutex là để bảo vệ tài nguyên được chia sẻ.
Vì vậy, chúng KHÔNG giống nhau ngay cả khi một số người luôn nói rằng mutex là một semaphore đặc biệt với giá trị ban đầu là 1. Bạn cũng có thể nói như thế này nhưng hãy lưu ý rằng chúng được sử dụng trong các trường hợp khác nhau. Đừng thay thế cái này bởi cái khác ngay cả khi bạn có thể làm điều đó.
x = getx(); y = gety(); z = x + y;
Vì một lý do nào đó, chúng ta sử dụng ba luồng để làm ba việc, lúc này thứ tự của các luồng rất quan trọng vì chúng ta không thể làm x + y
trừ khi getx
và gety
đã kết thúc. Nói một cách ngắn gọn, semaphore được sử dụng khi chúng ta quan tâm đến thứ tự thực hiện đa luồng.
x
và y
được hoàn thành, sau đó tính toán z = x + y
. Tôi biết java có CyclicBarrier
. Ngoài ra, tôi không chắc liệu tôi có thể nói mapreduce
là semaphore usecase hay không, bởi vì tôi không thể reduce
cho đến khi tất cả các map
s được hoàn thành.
Tất cả các câu trả lời trên đều có chất lượng tốt, nhưng điều này chỉ để tên memorize.The Mutex có nguồn gốc từ loại trừ lẫn nhau do đó bạn được thúc đẩy suy nghĩ của một khóa mutex như Mutual Exclusion giữa hai như trong chỉ một lúc, và nếu tôi Sở hữu nó, bạn chỉ có thể có nó sau khi tôi phát hành nó. Mặt khác, trường hợp như vậy không tồn tại đối với Semaphore chỉ giống như một tín hiệu giao thông (mà từ Semaphore cũng có nghĩa là).
Như đã chỉ ra, một semaphore có số đếm là một giống như một semaphore 'nhị phân', giống như một mutex.
Những điều chính mà tôi đã thấy các semaphores có số lượng lớn hơn một được sử dụng là các tình huống của nhà sản xuất / người tiêu dùng trong đó bạn có một hàng đợi có kích thước cố định nhất định.
Sau đó, bạn có hai semaphores. Semaphore đầu tiên ban đầu được đặt là số lượng mục trong hàng đợi và semaphore thứ hai được đặt thành 0. Nhà sản xuất thực hiện một hoạt động P trên semaphore đầu tiên, thêm vào hàng đợi. và thực hiện thao tác V vào thứ hai. Người tiêu dùng thực hiện một hoạt động P trên semaphore thứ hai, xóa khỏi hàng đợi, và sau đó thực hiện một hoạt động V trên thứ nhất.
Theo cách này, nhà sản xuất bị chặn bất cứ khi nào nó lấp đầy hàng đợi và người tiêu dùng bị chặn bất cứ khi nào hàng đợi trống.
Một mutex là một trường hợp đặc biệt của semaphore. Một semaphore cho phép một số luồng đi vào phần quan trọng. Khi tạo một semaphore, bạn xác định cách có thể cho phép các luồng trong phần quan trọng. Tất nhiên mã của bạn phải có khả năng xử lý một số quyền truy cập vào phần quan trọng này.
Semaphore nhị phân và Mutex là khác nhau. Từ quan điểm hệ điều hành, semaphore nhị phân và semaphore đếm được thực hiện theo cùng một cách và semaphore nhị phân có thể có giá trị 0 hoặc 1.
Mutex -> Chỉ có thể được sử dụng cho một và duy nhất mục đích loại trừ lẫn nhau cho một phần mã quan trọng.
Semaphore -> Có thể được sử dụng để giải quyết nhiều vấn đề. Một semaphore nhị phân có thể được sử dụng để báo hiệu và cũng giải quyết vấn đề loại trừ lẫn nhau. Khi được khởi tạo thành 0 , nó giải quyết vấn đề báo hiệu và khi khởi tạo thành 1 , nó giải quyết vấn đề loại trừ lẫn nhau .
Khi số lượng tài nguyên nhiều hơn và cần đồng bộ, chúng ta có thể sử dụng semaphore đếm.
Trong blog của tôi, tôi đã thảo luận chi tiết về các chủ đề này.
https://designpatterns-oo-cplusplus.blogspot.com/2015/07/synchronization-primosystem-mutex-and.html