SemaphoreSlim의 사용법을 이해해야합니다.


90

여기에 제가 가지고있는 코드가 있지만 무엇을하는지 이해가되지 않습니다 SemaphoreSlim.

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
    }
}

무엇 await를 수행 ss.WaitAsync();하고 ss.Release();있습니까?

한 번에 50 개의 스레드를 실행하고 다음과 같은 코드를 작성하면 한 번에 SemaphoreSlim ss = new SemaphoreSlim(10);10 개의 활성 스레드를 실행해야합니다.

10 개의 스레드 중 하나가 완료되면 다른 스레드가 시작됩니다. 내가 옳지 않다면 샘플 상황을 이해하도록 도와주세요.

await함께 필요 ss.WaitAsync();합니까? 무엇을 ss.WaitAsync();합니까?


3
한 가지주의 할 점은 "DoPollingThenWorkAsync ();"를 래핑해야한다는 것입니다. "try {DoPollingThenWorkAsync ();} finally {ss.Release ();}"에서 그렇지 않으면 예외가 해당 세마포를 영구적으로 고갈시킵니다.
Austin Salgat

태스크 외부 / 내부에서 각각 세마포어를 획득하고 해제하는 것이 조금 이상합니다. 작업 내에서 "await ss.WaitAsync ()"를 이동하면 차이가 있습니까?
Shane Lu

답변:


73

한 번에 50 개의 스레드를 실행하면 SemaphoreSlim ss = new SemaphoreSlim (10); 한 번에 10 개의 활성 스레드를 실행하도록 강제합니다.

맞아요; 세마포어를 사용하면 동시에이 작업을 수행하는 작업자가 10 명 이상이되지 않습니다.

WaitAsync세마포어를 호출 하면 해당 스레드에 해당 토큰에 대한 "액세스"가 주어 졌을 때 완료 될 작업이 생성됩니다. await-이 작업을 통해 "허용 된"경우 프로그램이 계속 실행되도록합니다. 를 호출하는 것보다 비동기 버전 Wait을 사용하는 것은 메서드가 동기가 아닌 비동기 상태를 유지하도록 보장하고 메서드가 async콜백으로 인해 여러 스레드에서 코드를 실행할 수 있다는 사실을 처리하는 데 중요합니다. 세마포어와의 자연스러운 스레드 친 화성이 문제가 될 수 있습니다.

참고 : 실제로 비동기 적이 지 않고 동기식이기 때문에 접미사 DoPollingThenWorkAsync가 없어야 Async합니다. 그냥 전화하십시오 DoPollingThenWork. 독자들에게 혼란을 줄 것입니다.


감사하지만 실행할 스레드를 지정하지 않으면 10.10 스레드 중 하나가 완료되면 스레드 점프가 다른 작업을 완료하거나 풀로 돌아갈 때 어떤 일이 발생합니까? 이것은 명확하지 않습니다 .. 그래서 장면 뒤에서 일어나는 일을 설명해주십시오.
Mou 2013

@Mou 그것에 대해 명확하지 않은 것은 무엇입니까? 코드는 현재 실행중인 작업이 10 개 미만일 때까지 기다립니다. 있을 때 다른 것을 추가합니다. 작업이 완료되면 완료되었음을 나타냅니다. 그게 다야.
Servy

실행할 스레드를 지정하지 않는 이점은 무엇입니까? 스레드가 너무 많으면 성능이 저하 될 수 있습니까? 그렇다면 왜 방해가됩니까 ... 10 스레드 대신 50 개의 스레드를 실행하면 성능이 왜 중요한지 ... 설명해 주시겠습니까? 감사
토마스

4
@Thomas 동시 스레드가 너무 많으면 스레드가 생산적인 작업을 수행하는 데 소비하는 것보다 컨텍스트 전환에 더 많은 시간을 소비합니다. 작업을 수행하는 대신 스레드를 관리하는 데 점점 더 많은 시간을 소비함에 따라 스레드가 증가함에 따라 처리량은 감소합니다. 적어도 스레드 수가 시스템의 코어 수를 훨씬 넘어 서면.
Servy

3
@Servy 작업 스케줄러 작업의 일부입니다. 작업! = 스레드. Thread.Sleep원래 코드는 작업 스케줄러를 뒤흔들 것이다. 코어에 비동기가 아니라면 비동기가 아닙니다.
Joseph Lennox

53

모퉁이에있는 유치원에서는 SemaphoreSlim을 사용하여 체육실에서 얼마나 많은 아이들이 놀 수 있는지 제어합니다.

그들은 방 밖에있는 바닥에 5 쌍의 발자국을 그렸습니다.

아이들이 도착하면, 그들은 신발을 무료 발자국에두고 방으로 들어갑니다.

게임을 마치면 나와서 신발을 모아서 다른 아이를 위해 슬롯을 "공개"합니다.

아이가 도착하고 남은 발자국이 없으면 다른 곳에서 놀거나 잠시 머물면서 가끔씩 확인합니다 (예 : FIFO 우선 순위 없음).

교사가 주위에있을 때 그녀는 복도 반대편에 5 개의 발자국이있는 추가 행을 "방출"하여 5 명의 어린이가 동시에 교실에서 놀 수 있도록합니다.

또한 SemaphoreSlim과 동일한 "함정"이 있습니다.

아이가 게임을 마치고 신발을 꺼내지 않고 방을 나가면 ( "릴리스"를 트리거하지 않음) 이론적으로는 빈 슬롯이 있더라도 슬롯이 차단 된 상태로 유지됩니다. 하지만 아이는 보통 말을 듣습니다.

때로는 한 두 명의 교활한 아이가 다른 곳에 신발을 숨기고 방에 들어갑니다. 모든 발자국이 이미 찍혔더라도 (즉, SemaphoreSlim은 방에있는 아이 수를 "정말"제어하지 않습니다).

방의 과밀로 인해 아이들이 울고 교사가 방을 완전히 닫는 경향이 있기 때문에 이것은 일반적으로 잘 끝나지 않습니다.


3
이런 종류의 답변이 제가 가장 좋아하는 것입니다.
내 스택 Overfloweth

OMG 이것은 유익하고 재미 있습니다!
Zonus

7

이 질문은 실제로 카운트 다운 잠금 시나리오와 관련이 있지만 SemaphoreSlim을 간단한 비동기 잠금으로 사용하려는 사람들을 위해 찾은이 링크를 공유 할 가치가 있다고 생각했습니다. 코딩을 더 깔끔하고 안전하게 만들 수있는 using 문을 사용할 수 있습니다.

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

나는 스왑했다 _isDisposed=true_semaphore.Release()케이스에 든 여러 번 호출있어 비록 그 폐기에 주위를.

또한 SemaphoreSlim은 재진입 잠금이 아닙니다. 즉, 동일한 스레드가 WaitAsync를 여러 번 호출하면 세마포어의 개수가 매번 감소된다는 점에 유의해야합니다. 간단히 말해 SemaphoreSlim은 스레드를 인식하지 않습니다.

코드 품질에 대한 질문과 관련하여 릴리스가 항상 릴리스되도록하기 위해 최종적으로 트라이 파이널 내에 릴리스를 배치하는 것이 좋습니다.


6
링크는 시간이 지남에 따라 사라지는 경향이 있으므로 링크 전용 답변을 게시하는 것은 바람직하지 않습니다. 가능하다면 핵심 요점이나 핵심 코드 블록을 답에 요약하는 것이 가장 좋습니다.
John
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.