2012-07-03 7 views
5

私はさまざまなソースからのメッセージを簡単なリストに入れるアプリケーションサービスを提供しています。独自のスレッドで実行されるサービスは、定期的にリスト内のすべてのメッセージをさまざまなファイルに処理します。ソースごとに1つのファイルが作成され、サイズごとに管理されます。リストをロックする前後に再確認する必要がありますか?

私の質問は、メッセージをチェックし、リストにアクセスするコードの周りにロックを実行する適切な方法についてです。リストにアクセスする場所は2つだけです。 1つはメッセージがリストに追加され、もう1つはメッセージがリストから処理リストにダンプされる場所です。

リストにメッセージを追加:

リストを処理
Public Sub WriteMessage(ByVal messageProvider As IEventLogMessageProvider, ByVal logLevel As EventLogLevel, ByVal message As String) 
    SyncLock _SyncLockObject 
     _LogMessages.Add(New EventLogMessage(messageProvider, logLevel, Now, message)) 
    End SyncLock 
End Sub 

Dim localList As New List(Of EventLogMessage) 
SyncLock _SyncLockObject 
    If (_LogMessages.Count > 0) Then 
     localList.AddRange(_LogMessages) 
     _LogMessages.Clear() 
    End If 
End SyncLock 

' process list into files... 

は、私の質問は以下のとおりです。私はリストを処理していたとき、私は以下を参照してください、二重のチェックを行う必要がありますか?なぜ?それともなぜですか?そして、ロックの外でリストのcountプロパティにアクセスする際に危険がありますか?メソッドの方が効率的か、効率的か?なぜ?それともなぜですか?

Dim localList As New List(Of EventLogMessage) 
If (_LogMessages.Count > 0) Then 
    SyncLock _SyncLockObject 
     If (_LogMessages.Count > 0) Then 
      localList.AddRange(_LogMessages) 
      _LogMessages.Clear() 
     End If 
    End SyncLock 
End If 

' process list into files... 

私は処理関数の外に、リストだけ成長できる、という事実与えられた二重のチェックを行う場合は、この特定のケースでは、それは問題ではないかもしれないことを理解しています。しかし、これは私の実際の例であり、私はスレッディングの詳細について学びたいと思っています。

は「アライグマ」、およびいくつかの実験的なプログラミングをありがとう、...任意の洞察力のために、事前にいくつかのさらなる研究の後


をありがとう、私はさらにいくつかの考えを持っています。

ReaderWriterLockSlimに関して、私はうまくいくように見える次の例を持っています。これにより、リスト内のメッセージ数や他のコードとの干渉なく、リスト内のメッセージ数を読むことができます。リストを処理したいときは、ロックを書き込みモードにアップグレードし、メッセージを処理リストにダンプし、読み取り/書き込みロックの外で処理して、追加または読み込みが必要な他のスレッドをブロックしません、より多くのメッセージ。

この例では、Typeを他のメタデータとともに使用していた前の例とは異なり、メッセージのためのより簡単な構成を使用しています。

Private _ReadWriteLock As New Threading.ReaderWriterLockSlim() 

Private Sub Process() 
    ' create local processing list 
    Dim processList As New List(Of String) 
    Try 
     ' enter read lock mode 
     _ReadWriteLock.EnterUpgradeableReadLock() 
     ' if there are any messages in the 'global' list 
     ' then dump them into the local processing list 
     If (_Messages.Count > 0) Then 
      Try 
       ' upgrade to a write lock to prevent others from writing to 
       ' the 'global' list while this reads and clears the 'global' list 
       _ReadWriteLock.EnterWriteLock() 
       processList.AddRange(_Messages) 
       _Messages.Clear() 
      Finally 
       ' alway release the write lock 
       _ReadWriteLock.ExitWriteLock() 
      End Try 
     End If 
    Finally 
     ' always release the read lock 
     _ReadWriteLock.ExitUpgradeableReadLock() 
    End Try 
    ' if any messages were dumped into the local processing list, process them 
    If (processList.Count > 0) Then 
     ProcessMessages(processList) 
    End If 
End Sub 

Private Sub AddMessage(ByVal message As String) 
    Try 
     ' enter write lock mode 
     _ReadWriteLock.EnterWriteLock() 
     _Messages.Add(message) 
    Finally 
     ' always release the write lock 
     _ReadWriteLock.ExitWriteLock() 
    End Try 
End Sub 

私はこの技術で唯一の問題は、開発者がロックの取得と解放について勤勉でなければならないということです。そうしないと、デッドロックが発生します。

これがSyncLockを使用するよりも効率的かどうかに関しては、私は本当に言えませんでした。この特定の例とその使用法については、いずれか十分であると信じています。他の誰かがそれを変更している間にカウントを読んでいるという理由で、 'coon'が与えたのは二重チェックではありません。この例では、SyncLockは同じ機能を提供します。しかし、若干複雑なシステムでは、複数のソースがリストを読み書きする可能性があるため、ReaderWriterLockSlimが理想的です。


BlockingCollectionリストに関して、次の例は上記のように動作します。

Private _Messages As New System.Collections.Concurrent.BlockingCollection(Of String) 

Private Sub Process() 
    ' process each message in the list 
    For Each item In _Messages 
     ProcessMessage(_Messages.Take()) 
    Next 
End Sub 

Private Sub AddMessage(ByVal message As String) 
    ' add a message to the 'global' list 
    _Messages.Add(message) 
End Sub 

シンプルそのもの...

+0

あなたはサービスのアーキテクチャに対してどれだけのコントロールを持っていますか?これは、MSMQを使用して重い持ち上げを行うことができたようです。 http://msdn.microsoft.com/en-us/library/ms789048.aspx – Trevor

+0

私はサービスを言うとき、アプリケーションの内部で開始されたクラスを意味します。スレッドは、Windowsサービスではなく、アプリケーションの他の部分によって与えられたメッセージを定期的に処理します。汎用化されたメッセージハンドラ(アプリケーション内に完全にカプセル化され、独自のバックグラウンドスレッドで実行される場合)。この「サービス」は、1)メッセージを作成するための簡単な方法を提供すること、および2)メッセージの記憶域が多すぎる記憶領域を消費するのを防ぐことを課されます。 また、アプリケーション間メッセージングの必要はありません。 –

+0

このアプリケーションでは、メッセージに誤った用語が使用されている可能性があります。ログされたイベントほどメッセージではありません。メッセージを作成したアプリケーションまたはモジュールに固有の、関連する「デバッグ」、「メッセージ」、「警告」または「エラー」ログイベント。 –

答えて

3

論:

スレッドが_SyncLockObjectは、ロックが解除されるのを待たなければならない、そのメソッドを再入力する他のすべてのスレッドがロックを取得したら。

したがって、ロックの前後のチェックは関係ありません。つまり、何の効果もありません。また、は安全ではありません、あなたは並行リストを使用していないためです。

1つのスレッドが最初のテストでCountをチェックして、別のスレッドがコレクションをクリアまたは追加している場合は、Collection was modified; enumeration operation may not execute.で例外が発生します。また、2回目のチェックは、(同期されているので)一度に1つのスレッドでしか実行できません。

これは、Addメソッドにも適用されます。ロックは1つのスレッドによって所有されていますが(実行フローがその行に達したことを意味します)、他のスレッドはリストを処理または追加できません。

アプリケーションの他の場所のリストから読み込んでいる場合は、ロックするように注意する必要があります。複雑な読み取り/書き込みシナリオ(カスタム同時収集など)の場合は、ReaderWriterLockSlimを使用することをお勧めします。

実践:それは、スレッドセーフであるため

は(すなわち、それは内部的に並行性を扱う)、BlockingCollectionを使用してください。

関連する問題