2016-12-05 10 views
6

난 당신과 같이 구성하면 SemaphoreSlim은 한 번에 하나의 스레드에 의해 실행되는 코드의 일부를 제한 할 것을 나타냅니다 SemaphoreSlim SemaphoreSlim MSDN 에 대한 문서 읽고 그러나SemaphoreSlim (.NET)이 동일한 스레드가 블록을 입력하지 못하도록합니까?

SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1); 

을, 그것은 '아무튼 t는 해당 코드에 스레드가 액세스하는 것을 중지하는지 나타냅니다. 이것은 비동기와 함께오고 기다리고 있습니다. 메서드에서 await을 사용하면 컨트롤에서 해당 메서드를 끝내고 완료된 작업이나 스레드가 반환됩니다. 예제에서는 비동기 버튼 핸들러가있는 버튼을 사용합니다. 'await'와 함께 다른 메소드 (Function1)를 호출합니다. 차례로 기능 1 내가 SemaphoreSlim이() 내 Task.Run 주변

await Task.Run(() => Function2(beginCounter)); 

를 호출합니다. 그것은 확실히 그것이 Function2에 점점에서 동일한 스레드를 중지 것처럼 보인다. 그러나 이것은 문서에서 (내가 읽었을 때) 보장 할 수는 없지만 그 값을 계산할 수 있는지 궁금합니다.

아래에 완전한 예를 게시했습니다.

감사합니다,

데이브

using System; 
using System.Threading; 
using System.Threading.Tasks; 
using System.Windows; 

namespace AsynchAwaitExample 
{ 
/// <summary> 
/// Interaction logic for MainWindow.xaml 
/// </summary> 
public partial class MainWindow : Window 
{ 
    private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1); 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    static int beginCounter = 0; 
    static int endCounter = 0; 
    /// <summary> 
    /// Suggest hitting button 3 times in rapid succession 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private async void button_Click(object sender, RoutedEventArgs e) 
    { 
     beginCounter++; 
     endCounter++; 
     // Notice that if you click fast, you'll get all the beginCounters first, then the endCounters 
     Console.WriteLine("beginCounter: " + beginCounter + " threadId: " + Thread.CurrentThread.ManagedThreadId); 
     await Function1(beginCounter); 
     Console.WriteLine("endCounter: " + endCounter + " threadId: " + Thread.CurrentThread.ManagedThreadId); 
    } 

    private async Task Function1(int beginCounter) 
    { 
     try 
     { 
      Console.WriteLine("about to grab lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter); 
      await _semaphoreSlim.WaitAsync(); // get rid of _semaphoreSlim calls and you'll get into beginning of Function2 3 times before exiting 
      Console.WriteLine("grabbed lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter); 
      await Task.Run(() => Function2(beginCounter)); 
     } 
     finally 
     { 
      Console.WriteLine("about to release lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter); 
      _semaphoreSlim.Release(); 
      Console.WriteLine("released lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter); 
     } 

    } 

    private void Function2(int beginCounter) 
    { 
     Console.WriteLine("Function2 start" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter); 
     Thread.Sleep(1000); 
     Console.WriteLine("Function2 end" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter); 
     return; 
    } 
} 
} 

샘플 출력은 당신이 버튼을 3 번을 클릭합니다. Function2는 주어진 카운터가 다시 시작되기 전에 항상 종료됩니다. 당신이 SemaphoreSlim 제거하면

beginCounter: 1 threadId: 9 
about to grab lock threadId: 9 beginCounter: 1 
grabbed lock threadId: 9 beginCounter: 1 
Function2 start threadId: 13 beginCounter: 1 
beginCounter: 2 threadId: 9 
about to grab lock threadId: 9 beginCounter: 2 
beginCounter: 3 threadId: 9 
about to grab lock threadId: 9 beginCounter: 3 
Function2 end threadId: 13 beginCounter: 1 
about to release lock threadId: 9 beginCounter: 1 
released lock threadId: 9 beginCounter: 1 
grabbed lock threadId: 9 beginCounter: 2 
Function2 start threadId: 13 beginCounter: 2 
endCounter: 3 threadId: 9 
Function2 end threadId: 13 beginCounter: 2 
about to release lock threadId: 9 beginCounter: 2 
released lock threadId: 9 beginCounter: 2 
endCounter: 3 threadId: 9 
grabbed lock threadId: 9 beginCounter: 3 
Function2 start threadId: 13 beginCounter: 3 
Function2 end threadId: 13 beginCounter: 3 
about to release lock threadId: 9 beginCounter: 3 
released lock threadId: 9 beginCounter: 3 
endCounter: 3 threadId: 9 

는 당신이 얻을 것이다 호출

beginCounter: 1 threadId: 10 
about to grab lock threadId: 10 beginCounter: 1 
grabbed lock threadId: 10 beginCounter: 1 
Function2 start threadId: 13 beginCounter: 1 
beginCounter: 2 threadId: 10 
about to grab lock threadId: 10 beginCounter: 2 
grabbed lock threadId: 10 beginCounter: 2 
Function2 start threadId: 14 beginCounter: 2 
beginCounter: 3 threadId: 10 
about to grab lock threadId: 10 beginCounter: 3 
grabbed lock threadId: 10 beginCounter: 3 
Function2 start threadId: 15 beginCounter: 3 
Function2 end threadId: 13 beginCounter: 1 
about to release lock threadId: 10 beginCounter: 1 
released lock threadId: 10 beginCounter: 1 
endCounter: 3 threadId: 10 
Function2 end threadId: 14 beginCounter: 2 
about to release lock threadId: 10 beginCounter: 2 
released lock threadId: 10 beginCounter: 2 
endCounter: 3 threadId: 10 

답변

10

the documentation에서 :

호출 스레드 또는 작업 ID를 적용하지 않습니다 SemaphoreSlim 클래스 Wait, WaitAsync 및 Release 메서드

인 다른 말로, 클래스는 어떤 스레드가 그것을 호출하는지 보지 않습니다. 그것은 단지 간단한 카운터입니다. 동일한 스레드가 세마포어를 여러 번 획득 할 수 있으며, 이는 여러 스레드가 세마포어를 획득 한 경우와 동일합니다. 나머지 스레드 수가 0으로 내려 가면 해당 스레드가 이미 세마포어를 획득 한 스레드 일 경우에도 Wait()을 호출하면 다른 스레드가 해당 세마포를 해제 할 때까지 차단됩니다.

따라서 async/await과 관련하여 await이 시작된 동일한 스레드에서 다시 시작될 수도 있고 다시 시작되지 않을 수도 있습니다. Wait()Release() 번의 통화가 균형을 유지하는 한 희망과 기대로 작동합니다.

예제에서 세마포어를 비동기 적으로 기다리며 스레드를 차단하지 않습니다. 두 번째로 버튼을 눌렀을 때 UI 스레드에 교착 상태가 발생하기 때문에 좋습니다.


관련 독서 : 재진입/재귀 잠금에 특히주의에
Resource locking between iterations of the main thread (Async/Await)
Why does this code not end in a deadlock
Locking with nested async calls

주, 특히 async/await와. 스레드 동기화는 그만큼 까다 롭습니다. 그 어려움은 단순화하기 위해 async/await이 설계된 것입니다. 그리고 대부분의 경우에 그렇게 크게 영향을줍니다. 그러나 당신이 아직 또 다른 동기화/잠금 메커니즘과 그것을 섞을 때 아닙니다.

+0

답변과 참조에 감사드립니다. – Dave