2017-10-15 2 views
2

私はスレッドセーフではない固定の数の "ブラウザ"を持っていますので、スレッドごとに使用する必要があります。一方、私はこれらのブラウザを使用するのを待っているスレッドの長いリストを持っています。だから私は私がのために特定のブラウザのインデックスを取得することができ、ブラウザごとに1 AutoResetEventを、持っているWaitHandle.WaitAnyスレッドが順序通りに入力できるようにします

WaitHandles = Enumerable.Range(0, Browsers.Count).Select(_ => new AutoResetEvent(true)).ToArray(); 

public readonly AutoResetEvent[] WaitHandles; 

そして、このようにそれらを初期化します。私は現在やっていることはAutoResetEvent配列を持っています各スレッド:

public Context WaitForBrowser(int i) 
{ 
    System.Diagnostics.Debug.WriteLine($">>> WILL WAIT: {i}"); 
    var index = WaitHandle.WaitAny(WaitHandles); 
    System.Diagnostics.Debug.WriteLine($">>> ENTERED: {i}"); 
    return new Context(Browsers[index], WaitHandles[index]); 
} 

これらのスレッドがリストにあるとパルティを持っているので、ここでiは、スレッド待ちの単なる指標であり、尿道序列私はデバッグの目的でこれを渡しています。 Contextは、処分されるときに待機ハンドル上のSetを呼び出す使い捨て品です。

WaitForBrowserへの呼び出しが順番に行われているので、すべての「>>> WILL WAIT:{i}」メッセージが正しい順序で表示されますが、私の出力を見ると、「>>> ENTERED: i}」のメッセージはランダムな順番(最初の数を除く)であるため、var index = WaitHandle.WaitAny(WaitHandler);行に到着したのと同じ順序で入力していません。

私の質問は、WaitForBrowserメソッドが呼び出された(「>>> ENTERED:{i}」というメッセージも同様に)注文されるようにこれを変更する方法はありますか?

+0

[?C#でFIFOの順序を保証する同期クラスがあります]の可能な重複(https://stackoverflow.com/questions/961869/is-there-a-synchronization-class-that-保証 - fifo-order-in-c) –

+0

@JohnWu同じ質問ではない(質問の本文を参照) – Juan

答えて

0

AutoResetEventの配列の代わりにセマフォを使用することを検討しましたか?

(セマフォ)のスレッドを待っているの順序を通報しますが、ここで議論されました: Guaranteed semaphore order?

+0

私は持っていますが、インデックスを取得する方法はわかりません( 'WaitOne'メソッドは' bool'を返します)。 – Juan

1

アウトオブボックスソリューションがあるようには思えないので、私はの修正バージョンを使用して終了しましたthis solution

public class SemaphoreQueueItem<T> : IDisposable 
{ 
    private bool Disposed; 
    private readonly EventWaitHandle WaitHandle; 
    public readonly T Resource; 

    public SemaphoreQueueItem(EventWaitHandle waitHandle, T resource) 
    { 
     WaitHandle = waitHandle; 
     Resource = resource; 
    } 

    public void Dispose() 
    { 
     if (!Disposed) 
     { 
      Disposed = true; 
      WaitHandle.Set(); 
     } 
    } 
} 

public class SemaphoreQueue<T> : IDisposable 
{ 
    private readonly T[] Resources; 
    private readonly AutoResetEvent[] WaitHandles; 
    private bool Disposed; 
    private ConcurrentQueue<TaskCompletionSource<SemaphoreQueueItem<T>>> Queue = new ConcurrentQueue<TaskCompletionSource<SemaphoreQueueItem<T>>>(); 

    public SemaphoreQueue(T[] resources) 
    { 
     Resources = resources; 
     WaitHandles = Enumerable.Range(0, resources.Length).Select(_ => new AutoResetEvent(true)).ToArray(); 
    } 

    public SemaphoreQueueItem<T> Wait(CancellationToken cancellationToken) 
    { 
     return WaitAsync(cancellationToken).Result; 
    } 

    public Task<SemaphoreQueueItem<T>> WaitAsync(CancellationToken cancellationToken) 
    { 
     var tcs = new TaskCompletionSource<SemaphoreQueueItem<T>>(); 
     Queue.Enqueue(tcs); 

     Task.Run(() => WaitHandle.WaitAny(WaitHandles.Concat(new[] { cancellationToken.WaitHandle }).ToArray())).ContinueWith(task => 
     { 
      if (Queue.TryDequeue(out var popped)) 
      { 
       var index = task.Result; 

       if (cancellationToken.IsCancellationRequested) 
        popped.SetResult(null); 
       else 
        popped.SetResult(new SemaphoreQueueItem<T>(WaitHandles[index], Resources[index])); 
      } 
     }); 

     return tcs.Task; 
    } 

    public void Dispose() 
    { 
     if (!Disposed) 
     { 
      foreach (var handle in WaitHandles) 
       handle.Dispose(); 

      Disposed = true; 
     } 
    } 
} 
関連する問題