2016-12-05 16 views
6

私はあなたのようにそれを設定した場合SemaphoreSlimは一度に1つのスレッドで実行されるコードのセクションを制限することを示しているSemaphoreSlim SemaphoreSlim MSDN のためのドキュメント読んでいる:しかし、それはdoesnのSemaphoreSlim(.NET)は、同じスレッドがブロックに入ることを防止しますか?

SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1); 

をt 同じスレッドがそのコードにアクセスするのを停止するかどうかを示します。これは非同期であり、待ち受けています。あるメソッドでawaitを使用すると、コントロールはそのメソッドを終了し、完了したタスクまたはスレッドがあれば返します。私の例では、非同期ボタンハンドラを持つボタンを使用します。それは 'await'で別のメソッド(Function1)を呼び出します。 Function1が順番に呼び出す

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

私の周りにTask.Run()私はSemaphoreSlimを持っています。それは、同じスレッドが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のメソッド

In言い換えれば、クラスはどのスレッドがそれを呼び出すのか見ない。それは単純なカウンターです。同じスレッドがセマフォを複数回取得することができます。これは、複数のスレッドがセマフォを取得した場合と同じになります。残っているスレッド数が0になっていれば、既にそのスレッドがセマフォーを獲得したスレッドであっても、を呼び出すと、他のスレッドがセマフォを解放するまでブロックされます。

したがって、async/awaitに関しては、awaitが開始された同じスレッドで再開してもしなくてもかまいません。 Wait()Release()のコールを均衡させておく限り、期待どおりに動作します。

この例では、セマフォを非同期的に待っているため、スレッドをブロックしません。あなたがボタンを2度押したときに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

関連する問題