1

昨日、使用している単純なキャッシュ オブジェクトにマルチスレッドの問題があることを発見しました。

 If Dictionary.Contains(lsKey.ToLower) Then 'if rate cached, then return value
      lvResult = Dictionary.Item(lsKey.ToLower)

  Else 'else retrieve from database, store, and return value
      lvResult = GetRateFromDB(voADO,
                               veRateType,
                               vdEffDate)
      Dictionary.Add(lsKey.ToLower, lvResult)

  End If

この問題は、asp.net Web サイトで発見されました。エラーメッセージは、「既に存在するハッシュテーブルに値を追加しようとしています。上記のコードからわかるように、これが発生する可能性は間違いなく終了します。私はウェイトハンドルにある程度精通しており、それらが問題を解決すると考えていました。 .だから私はクラスレベルで私のwaithandleを宣言しました:

private Shared _waitHandle as new AutoResetEvent(True)

次に、問題のあるコードの特定のセクションで:

_waitHandle.Wait()
If Dictionary.Contains(lsKey.ToLower) Then 'if rate cached, then return value
    lvResult = Dictionary.Item(lsKey.ToLower)

Else 'else retrieve from database, store, and return value
    lvResult = GetRateFromDB(voADO,
                             veRateType,
                             vdEffDate)
     Dictionary.Add(lsKey.ToLower, lvResult)
End If
_waitHandle.Set()

何らかの理由で、上記の次のコードは常にブロックされていました。最初のスレッドでさえ、コードにアクセスしました。私はしばらく物事をいじり、コンストラクターで待機ハンドルをシグナル状態に設定しようとしましたが、それを機能させることはできませんでした。

私は代わりに次のものを使用することになりますが、これは正常に機能します。

SyncLock loLock
    If Dictionary.Contains(lsKey.ToLower) Then 'if rate cached, then return value
        lvResult = Dictionary.Item(lsKey.ToLower)

    Else 'else retrieve from database, store, and return value
        lvResult = GetRateFromDB(voADO,
                                 veRateType,
                                 vdEffDate)
        Dictionary.Add(lsKey.ToLower, lvResult)

    End If
End SyncLock

だから私は2つの質問があります:

  1. waithandle ソリューションが機能しなかったのはなぜですか?
  2. SynLock は、この場合に使用する正しい/最適化されたロック タイプですか?
4

2 に答える 2

1

シグナルが送られるまで、1 つの待機ハンドルがブロックされます。アクセスを取得する最初のスレッドをブロックしないようにするには、その待機ハンドルに信号を送るための何かが必要です。待機ハンドルを作成したコンストラクターでハンドルを通知していれば、うまくいくと思います。待機ハンドル内にシグナル用のスロットがあると考えてください。待機を呼び出すスレッドは、待機呼び出しを終了する前にシグナルを消費できるようになるまで待機します。

2 この場合、使用するのに最適なロックではない可能性があります。2 つのスレッドが既にキャッシュにある値を読み込もうとすると、もう一方が完了するまで一方がブロックされます。代わりにリーダーライター ロックを使用します。このようにして、複数のスレッドが同時にキャッシュを読み取ることができ、必要に応じて書き込みにアップグレードできます。

キャッシュへの同じ値の複数のロードを気にしない場合は、concurrentdictionary を使用できます。まだロードされていない値を必要とするスレッドは、値をロードしてから tryadd を呼び出します。複数のスレッドが同じアンロードされた値に同時にアクセスしようとした場合、すべてのスレッドが GetRateFromDB を呼び出す作業を行います。

于 2013-08-21T15:07:33.363 に答える
1

Windows 用語:

  • イベント」は、あるスレッド (またはプロセス) が別のスレッド (またはプロセス) にシグナルを送ることを可能にします。
  • セマフォ」はイベントに似ていますが、特定の数のスレッドにシグナルを送る (ウェイクする) ために使用できます。
  • クリティカル セクション」は、コード ブロックへの同時アクセスを防止するために使用されます。
  • スリムロック」はクリティカルセクションに似ていますが、通常、ロックを所有するスレッドが複数回入ることを許可しません (クリティカルセクションを使用すると許可されます)。
  • Reader Writer」ロックを使用すると、オブジェクトを排他モード (クリティカル セクションと同様) または複数のスレッドが同じブロックを実行できる共有モードでロックする柔軟性が得られます。
  • 完全を期すために、クリティカル セクションに非常によく似た「 Mutex 」もありますが、プロセス間で共有できます。

したがって、AutoResetEvent上記を使用することは、あなたが望むものではありません。スレッドがメソッドの 1 つを呼び出すとWait、別のスレッドからシグナルを受信するまでブロックされます。誰もあなたに信号を送らないので、永遠に待ちます。

このSyncLockステートメントは、内部でクリティカル セクションを使用し、スレッドが同時に同じコード チャンクに入るのを防ぎます。これにより、必要な保護が得られます。ただし、 Dictionary オブジェクトへのすべてのアクセスを保護して破損を防ぐ必要があるため、 Dictionary オブジェクトを使用するすべての場所でロックを使用する必要があります。

すでに述べたようにConcurrentDictionary、高度に調整された Reader Writer ロックが組み込まれているため、この場合は を使用することをお勧めします。したがって、コード ベース全体に多数のロックを追加する必要はありません。しかし、私のコメントで述べたように、ConcurrentDictionaryを使用すると、競合状態が発生する可能性があります。

于 2013-08-21T15:45:54.653 に答える