Cần hiểu cách sử dụng SemaphoreSlim


90

Đây là mã tôi có nhưng tôi không hiểu những gì SemaphoreSlimđang làm.

async Task WorkerMainAsync()
{
    SemaphoreSlim ss = new SemaphoreSlim(10);
    List<Task> trackedTasks = new List<Task>();
    while (DoMore())
    {
        await ss.WaitAsync();
        trackedTasks.Add(Task.Run(() =>
        {
            DoPollingThenWorkAsync();
            ss.Release();
        }));
    }
    await Task.WhenAll(trackedTasks);
}

void DoPollingThenWorkAsync()
{
    var msg = Poll();
    if (msg != null)
    {
        Thread.Sleep(2000); // process the long running CPU-bound job
    }
}

Điều gì đang chờ đợi ss.WaitAsync();ss.Release();làm gì?

Tôi đoán rằng nếu tôi chạy 50 chủ đề một lúc rồi viết mã như thế SemaphoreSlim ss = new SemaphoreSlim(10);thì nó sẽ bị buộc phải chạy 10 chủ đề hoạt động cùng một lúc.

Khi một trong 10 luồng hoàn thành thì một luồng khác sẽ bắt đầu. Nếu tôi không đúng thì hãy giúp tôi hiểu với tình huống mẫu.

Tại sao awaitcần thiết cùng với ss.WaitAsync();? Làm gì ss.WaitAsync();?


3
Một điều cần lưu ý là bạn thực sự nên bọc "DoPollingThenWorkAsync ();" trong "try {DoPollingThenWorkAsync ();} cuối cùng là {ss.Release ();}", nếu không các ngoại lệ sẽ vĩnh viễn bỏ đói semaphore đó.
Austin Salgat

Tôi cảm thấy hơi lạ khi chúng tôi thu nhận và giải phóng semaphore bên ngoài / bên trong nhiệm vụ tương ứng. Việc di chuyển "await ss.WaitAsync ()" vào bên trong tác vụ có tạo ra bất kỳ sự khác biệt nào không?
Shane Lu

Câu trả lời:


73

tôi đoán rằng nếu tôi chạy 50 chủ đề cùng một lúc thì mã như SemaphoreSlim ss = new SemaphoreSlim (10); sẽ buộc chạy 10 chủ đề hoạt động cùng một lúc

Đúng rồi; việc sử dụng semaphore đảm bảo rằng sẽ không có nhiều hơn 10 công nhân làm công việc này cùng một lúc.

Việc gọi WaitAsynctrên semaphore tạo ra một nhiệm vụ sẽ được hoàn thành khi luồng đó đã được cấp "quyền truy cập" vào mã thông báo đó. await-ing đó nhiệm vụ cho phép chương trình tiếp tục thực hiện khi nó được "cho phép" làm như vậy. Có một phiên bản không đồng bộ, thay vì gọi Wait, là điều quan trọng để đảm bảo rằng phương thức vẫn không đồng bộ, thay vì đồng bộ, cũng như xử lý thực tế rằng một asyncphương thức có thể thực thi mã trên một số luồng, do các lệnh gọi lại, v.v. mối quan hệ chủ đề tự nhiên với các bán kết có thể là một vấn đề.

Một lưu ý phụ: DoPollingThenWorkAsynckhông nên có hậu tố Asyncvì nó không thực sự không đồng bộ, nó đồng bộ. Chỉ cần gọi nó DoPollingThenWork. Nó sẽ giảm bớt sự nhầm lẫn cho người đọc.


cảm ơn nhưng hãy cho tôi biết điều gì sẽ xảy ra khi chúng tôi chỉ định không có luồng nào để chạy nói 10. khi một trong 10 luồng kết thúc thì luồng đó lại nhảy vào kết thúc công việc khác hoặc quay trở lại nhóm? điều này không rõ ràng cho lắm .... vì vậy hãy giải thích những gì xảy ra đằng sau hiện trường.
Mou

@Mou Có gì không rõ ràng về nó? Mã đợi cho đến khi có ít hơn 10 tác vụ hiện đang chạy; khi có, nó thêm một. Khi một nhiệm vụ kết thúc, nó chỉ ra rằng nó đã hoàn thành. Đó là nó.
Servy

lợi thế của việc chỉ định không có luồng nào để chạy. nếu quá nhiều luồng có thể cản trở hiệu suất? nếu có thì tại sao lại cản trở ... nếu tôi chạy 50 chủ đề thay vì 10 chủ đề thì tại sao hiệu suất lại quan trọng ... u có thể giải thích. cảm ơn
Thomas

4
@Thomas Nếu bạn có quá nhiều luồng đồng thời thì các luồng sẽ dành nhiều thời gian cho việc chuyển đổi ngữ cảnh hơn là dành cho công việc hiệu quả. Thông lượng giảm xuống khi các luồng tăng lên khi bạn ngày càng dành nhiều thời gian hơn để quản lý các luồng thay vì thực hiện công việc, ít nhất là khi số luồng của bạn vượt quá số lõi trên máy.
Servy

3
@Servy Đó là một phần công việc của bộ lập lịch tác vụ. Tasks! = Chủ đề. Trong Thread.Sleepmã gốc sẽ phá hủy bộ lập lịch tác vụ. Nếu bạn không đồng bộ với lõi, bạn không đồng bộ.
Joseph Lennox

54

Trong vườn trẻ xung quanh góc, họ sử dụng SemaphoreSlim để kiểm soát bao nhiêu đứa trẻ có thể chơi trong phòng thể dục.

Họ vẽ trên sàn nhà, bên ngoài căn phòng, 5 cặp dấu chân.

Khi những đứa trẻ đến nơi, chúng để lại đôi giày của chúng trên một đôi chân tự do và bước vào phòng.

Sau khi chơi xong, chúng đi ra, lấy giày và "thả" một chỗ cho một đứa trẻ khác.

Nếu một đứa trẻ đến và không còn dấu chân, chúng sẽ chơi ở nơi khác hoặc chỉ ở lại một lúc và thỉnh thoảng kiểm tra (tức là không có ưu tiên FIFO).

Khi một giáo viên ở xung quanh, cô ấy "thả" thêm một hàng 5 dấu chân ở phía bên kia của hành lang để 5 đứa trẻ khác có thể chơi trong phòng cùng một lúc.

Nó cũng có những “cạm bẫy” tương tự SemaphoreSlim ...

Nếu một đứa trẻ chơi xong và rời khỏi phòng mà không lấy giày (không kích hoạt "thả") thì khe vẫn bị chặn, mặc dù về mặt lý thuyết là có một khe trống. Tuy nhiên, đứa trẻ thường bị chỉ trích.

Đôi khi một hoặc hai đứa trẻ lén lút giấu giày ở nơi khác và vào phòng, ngay cả khi tất cả các dấu chân đã được lấy sẵn (tức là SemaphoreSlim không "thực sự" kiểm soát có bao nhiêu đứa trẻ trong phòng).

Điều này thường không kết thúc tốt đẹp, vì phòng quá đông có xu hướng kết thúc bằng cách trẻ em khóc và giáo viên đóng cửa hoàn toàn phòng.


3
Những loại câu trả lời là yêu thích của tôi.
Ngăn xếp của tôi tràn

OMG đây là thông tin và hài hước như heck!
Zonus

7

Mặc dù tôi chấp nhận câu hỏi này thực sự liên quan đến kịch bản khóa đếm ngược, nhưng tôi nghĩ rằng điều đáng để chia sẻ liên kết này mà tôi đã phát hiện ra cho những người muốn sử dụng SemaphoreSlim làm khóa không đồng bộ đơn giản. Nó cho phép bạn sử dụng câu lệnh using có thể giúp mã hóa gọn gàng hơn và an toàn hơn.

http://www.tomdupont.net/2016/03/how-to-release-semaphore-with-using.html

Tôi đã hoán đổi _isDisposed=true_semaphore.Release()xung quanh trong Dispose của nó mặc dù trong trường hợp bằng cách nào đó nó được gọi nhiều lần.

Ngoài ra, điều quan trọng cần lưu ý là SemaphoreSlim không phải là một khóa quay lại, có nghĩa là nếu cùng một luồng gọi WaitAsync nhiều lần thì số lượng semaphore có được giảm đi mỗi lần. Trong ngắn hạn SemaphoreSlim không biết Thread.

Về chất lượng mã câu hỏi, tốt hơn nên đặt Bản phát hành trong vòng thử nghiệm cuối cùng để đảm bảo nó luôn được phát hành.


6
Không thể đăng câu trả lời chỉ có liên kết vì các liên kết có xu hướng chết theo thời gian, do đó khiến câu trả lời trở nên vô giá trị. Nếu có thể, tốt nhất bạn nên tóm tắt các điểm chính hoặc khối mã chính vào câu trả lời của mình.
John
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.