2009-08-19 33 views
6

Chỉnh sửa: Tôi muốn bào chữa sự điên rồ tạm thời cho việc hỏi câu hỏi này, nhưng nó có ý nghĩa vào thời điểm đó (xem chỉnh sửa 2 bên dưới).WaitHandle.WaitAny và Semaphore lớp

Đối với dự án .NET 3.5, tôi có hai loại tài nguyên (R1R2) mà tôi cần kiểm tra tính khả dụng của. Mỗi loại tài nguyên có thể có (nói) 10 trường hợp bất kỳ lúc nào.

Khi một trong hai loại tài nguyên trở nên khả dụng, chuỗi công nhân của tôi cần phải thức dậy (có một số lượng đề tài thay đổi). Trong quá trình thực hiện trước đó, chỉ có một loại tài nguyên, mà tôi đã sử dụng Semaphore để kiểm tra tính khả dụng.

Bây giờ tôi cần phải chờ hai Semaphores riêng biệt (S1S2) theo dõi tính khả dụng của tài nguyên.

WaitHandle[] waitHandles = new WaitHandle[] { s1, s2 }; 
int signalledHandle = WaitHandle.WaitAny(waitHandles); 

switch (signalledHandle) 
{ 
    case 0: 
     // Do stuff 
     s1.Release(); 
    case 1: 
     // Do stuff 
     s2.Release(); 
} 

Tuy nhiên, có một vấn đề với điều này. Từ các tài liệu MSDN trên WaitAny:

Nếu có nhiều hơn một đối tượng trở nên hiệu trong suốt cuộc gọi, giá trị trả về là chỉ số mảng của đối tượng hiệu với giá trị chỉ số nhỏ nhất của tất cả các hiệu đối tượng .

Điều này cho thấy rằng tôi có thể giảm cả số lượng Semaphore của mình xuống 1 sau khi gọi WaitAny. Bởi vì signalledHandle sẽ chỉ ra rằng s1 đã được báo hiệu, tôi sẽ bắt đầu sử dụng tài nguyên R1 và phát hành nó khi tôi hoàn tất. Tuy nhiên, vì tôi không biết liệu S2 có được báo hiệu hay không, tính khả dụng trên tài nguyên này hiện có thể bị tắt. Nếu điều này xảy ra 10 lần, Semaphore của tôi sẽ vĩnh viễn 'trống' và tài nguyên R2 sẽ không còn được sử dụng nữa.

Cách tốt nhất để giải quyết vấn đề này là gì? Tôi có nên chuyển từ sử dụng hai semaphores sang các bộ đếm đơn giản và một AutoResetEvent để báo hiệu khi một trong hai bộ đếm được thay đổi không? Tôi có thiếu một số cách tiếp cận thanh lịch hơn?

Sửa 1:
Theo Ravadre, chỉ có một trong những Semaphores sẽ thực sự được thay đổi sau khi WaitAny. Sửa đổi một chút ví dụ của anh ta dường như xác nhận điều này, nhưng liệu có ai có thể chỉ cho tôi một số tài liệu chính thức xác định điều này không?

Chỉnh sửa 2:
Tôi đã nghĩ về điều này trên đường về nhà. Chỉ sau đó tôi mới nhận ra rằng điều này phải đúng với WaitAny để sử dụng. Vấn đề này sẽ không bị hạn chế đối với Semaphores, nhưng chỉ là về bất kỳ loại đối tượng đồng bộ hóa nào, làm cho WaitAny thực tế vô ích.

+0

Tôi đã thêm nguồn có giá trị (mặc dù không chính thức) mà bạn có thể muốn kiểm tra. –

Trả lời

5

Nếu tôi hiểu vấn đề của bạn một cách chính xác, tôi nghĩ rằng giải pháp của bạn là hoàn toàn ổn, và bạn chỉ là giải thích báo giá msdn.Khi gọi WaitHandle.WaitAny() bạn sẽ nhận được các chỉ số thấp nhất, nhưng bạn sẽ khóa trên chỉ có một WaitHandle (semaphore trong trường hợp này), kiểm tra mẫu này mã:


Semaphore s1 = new Semaphore(1, 2); 
Semaphore s2 = new Semaphore(1, 2); 

WaitHandle[] handles = new WaitHandle[] { s1, s2 }; 

int x = WaitHandle.WaitAny(handles); 

int prevS1 = s1.Release(); 
int prevS2 = s2.Release(); 

Trong kịch bản này, prevS1 sẽ bằng 0, bởi vì semaphore s1 "đã được chờ", vì vậy số lượt truy cập đã được giảm xuống 0, trong khi prevS2 sẽ bằng 1, vì trạng thái của nó không thay đổi do phương thức khởi tạo (Release() trả về bộ đếm trước khi phát hành, vì vậy trả về 1 có nghĩa là 1, bây giờ là 2 ").

Tài nguyên khác mà bạn có thể muốn xem: http://www.albahari.com/threading/part2.aspx#_Wait_Handles. Mặc dù nó không phải là một nguồn "chính thức", tôi nghĩ không có lý do gì để thấy nó không đáng tin cậy.

+0

Trong ví dụ này, bạn đang phát hành cả hai Semaphores vô điều kiện. Một trích dẫn từ MSDN: "Đó là trách nhiệm của lập trình viên để đảm bảo rằng các chủ đề không phát hành semaphore quá nhiều lần." Nếu bạn thực thi chuỗi 'WaitAny'/'Release' này hai lần,' s2.Release() 'sẽ tăng một ngoại lệ: * Thêm số đếm cho semaphore sẽ làm cho nó vượt quá số lượng tối đa. * – Thorarin

+0

Yup, nhưng đó là mẫu, đó là lý do tại sao tôi đã dùng các semaphores tức thời để có max counter == 2, vì vậy tôi biết rằng tôi có thể phát hành chúng một lần, mà không sợ ngoại lệ, những gì tôi đang chứng minh ở đây là mặc dù cả hai semaphores được phát hành (counter > 0) và WaitAny() trả về 0 (từ báo giá của bạn - chỉ số nhỏ nhất) chỉ semaphore thứ nhất bị khóa, trong khi một semaphore khác bị bỏ lại mà không thay đổi. –

+0

Sửa đổi tương tự cũng dường như hỗ trợ yêu cầu của bạn rằng chỉ có một Semaphore sẽ bị thay đổi (cá nhân tôi thấy * bị khóa * một chút bối rối trong ngữ cảnh semaphore), nhưng tôi muốn xem một số thông số chính thức cho biết điều này được đảm bảo. – Thorarin

0

Vì mục đích của bạn, khi gọi phương thức WaitHandle.WaitAny(), kết quả không quan trọng. Điều quan trọng là WaitHandle được báo hiệu, vì vậy bạn cần phải cố gắng để có được khóa/đồng bộ hóa một lần nữa.

void Main() { 
var semaphoreOne = new SemaphoreSlim(0, 1); 
var semaphoreTwo = new SemaphoreSlim(0, 1); 

ReleaseSemaphoreAfterWhile(semaphoreOne); 

bool firstAccepted; 
bool secondAccepted = false; 
while ((firstAccepted = semaphoreOne.Wait(0)) == false && 
    (secondAccepted = semaphoreTwo.Wait(0)) == false) { 
    var waitHandles = new [] { 
    semaphoreOne.AvailableWaitHandle, semaphoreTwo.AvailableWaitHandle 
    }; 
    WaitHandle.WaitAny(waitHandles); 
    Console.WriteLine("SemaphoreOne Before Lock = " + semaphoreOne.CurrentCount); 
    Console.WriteLine("SemaphoreTwo Before Lock = " + semaphoreTwo.CurrentCount); 
} 

if (firstAccepted) { 
    Console.WriteLine("semaphore 1 was locked"); 
} else if (secondAccepted) { 
    Console.WriteLine("semaphore 2 was locked"); 
} else { 
    throw new InvalidOperationException("no semaphores were signaled"); 
} 
} 

Random rd = new Random(); 
public void ReleaseSemaphoreAfterWhile(SemaphoreSlim semaphore) { 
var sleepWork =(int)rd.Next(100, 1000); 
ThreadPool.QueueUserWorkItem(t => { 
    Thread.Sleep(10000 + sleepWork); 
    semaphore.Release(); 
}); 
} 

Có phòng cho triển khai khác với ý tưởng tương tự/logic, nhưng sử dụng vòng lặp while trong cách mà bạn đảm bảo rằng chỉ có một semaphore đang xảy ra để mua, và nếu không có phòng, nó khóa thread cho đến khi bất kỳ WaitHandle nào được báo hiệu - xem xét phương thức dụ .Release().

Thật không may (như được nêu trong các nhận xét) chúng là một số hiểu lầm về đồng bộ hóa chuỗi trong web, nhưng mã ở trên sẽ giúp bạn giải quyết vấn đề của mình.

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