4

マルチスレッドサーバーでのデータ競合を防止しようとしています。私の問題は次のとおりです。がありList<RServer>、タイプRServerはいくつかのフィールドを持つクラスです。これで、サーバーには複数のスレッドがすべて同時に実行され、それらはList(アイテムの追加)と個々のRServerインスタンス(フィールドの変更)の両方を変更できます。

したがって、私の戦略はreadonly object RServerLock = new object( )、各RServerインスタンスでを作成し、さらにaを作成し、(またはインスタンス)readonly object RServerListLock = new object( )のいずれかを変更するすべてのコードをで囲むことです。これは安全ですか?別のスレッドがロックしているときにスレッドがロックしようとするとどうなりますか?ListRServerlockRServerLock

4

6 に答える 6

8

ロックが競合している場合、2 番目のスレッドは最初のスレッドがロックを解放するまで待機する必要があります。

あなたの計画はほとんど問題ないように思えますが、最新の値と一貫した値を確実に取得するために、データを読み取るときにもロックする必要があります。そうしないと、1 つのスレッドでいくつかの値を書き込む途中で、新しい値の一部 (すべてではない可能性があります) と古い値が別のスレッドで同時に表示される可能性があります。

これをできるだけ避けることができれば、人生は楽になります:) 不変型はスレッド化をはるかに簡単にします。

同時に 2 つのロックを必要とするコードがある場合 (たとえば、RServer を 1 つ追加し、別の RServer をアトミックに変更するなど)、常に同じ順序でロックを取得する必要があることを忘れないでください。ロック A を保持している間に B をロックし、別のスレッドがロック B を保持している間にロック A を取得しようとすると、デッドロックが発生します。

詳細については、私のスレッド チュートリアルまたはJoe Albahari のチュートリアルを参照してください。また、並行処理に興味がある場合は、Joe Duffy の優れた本が間もなく出版されます。

于 2008-10-21T22:09:41.850 に答える
4

ReaderWriterLock の最有力候補のようです。元の ReaderWriterLock にはパフォーマンスの問題があるため、使用するのに最適なクラス (ランタイムでサポートされている場合、3.0 以降と思われます) は ReaderWriterLockSlim です。

MSDN マガジンの著者の 1 人も RWLS クラスに関する問題に遭遇しました。ここでは詳しく説明しませんが、ここで確認できます

次のコードが IDisposable の純粋主義者の怒りを買うことは承知していますが、実際には素晴らしい構文糖衣になることもあります。いずれにせよ、次のものが役立つ場合があります。

    /// <summary>
    /// Opens the specified reader writer lock in read mode,
    /// specifying whether or not it may be upgraded.
    /// </summary>
    /// <param name="slim"></param>
    /// <param name="upgradeable"></param>
    /// <returns></returns>
    public static IDisposable Read(this ReaderWriterLockSlim slim, bool upgradeable)
    {
        return new ReaderWriterLockSlimController(slim, true, upgradeable);
    } // IDisposable Read

    /// <summary>
    /// Opens the specified reader writer lock in read mode,
    /// and does not allow upgrading.
    /// </summary>
    /// <param name="slim"></param>
    /// <returns></returns>
    public static IDisposable Read(this ReaderWriterLockSlim slim)
    {
        return new ReaderWriterLockSlimController(slim, true, false);
    } // IDisposable Read

    /// <summary>
    /// Opens the specified reader writer lock in write mode.
    /// </summary>
    /// <param name="slim"></param>
    /// <returns></returns>
    public static IDisposable Write(this ReaderWriterLockSlim slim)
    {
        return new ReaderWriterLockSlimController(slim, false, false);
    } // IDisposable Write

    private class ReaderWriterLockSlimController : IDisposable
    {
        #region Fields

        private bool _closed = false;
        private bool _read = false;
        private ReaderWriterLockSlim _slim;
        private bool _upgrade = false;

        #endregion Fields

        #region Constructors

        public ReaderWriterLockSlimController(ReaderWriterLockSlim slim, bool read, bool upgrade)
        {
            _slim = slim;
            _read = read;
            _upgrade = upgrade;

            if (_read)
            {
                if (upgrade)
                {
                    _slim.EnterUpgradeableReadLock();
                }
                else
                {
                    _slim.EnterReadLock();
                }
            }
            else
            {
                _slim.EnterWriteLock();
            }
        } //  ReaderWriterLockSlimController

        ~ReaderWriterLockSlimController()
        {
            Dispose();
        } //  ~ReaderWriterLockSlimController

        #endregion Constructors

        #region Methods

        public void Dispose()
        {
            if (_closed)
                return;
            _closed = true;

            if (_read)
            {
                if (_upgrade)
                {
                    _slim.ExitUpgradeableReadLock();
                }
                else
                {
                    _slim.ExitReadLock();
                }
            }
            else
            {
                _slim.ExitWriteLock();
            }

            GC.SuppressFinalize(this);
        } // void Dispose

        #endregion Methods
    } // Class ReaderWriterLockSlimController

それを拡張メソッド クラス (public static class [Name]) に入れて、次のように使用します。

using(myReaderWriterLockSlim.Read())
{
  // Do read operations.
}

または

using(myReaderWriterLockSlim.Read(true))
{
  // Read a flag.
  if(flag)
  {
    using(myReaderWriterLockSlim.Write()) // Because we said Read(true).
    {
      // Do read/write operations.
    }
  }
}

または

using(myReaderWriterLockSlim.Write()) // This means you can also safely read.
{
  // Do read/write operations.
}
于 2008-10-22T05:21:36.270 に答える
2

スレッドが既にロックされているオブジェクトをロックしようとすると、ロックが解除されるまでブロックされます。ロックはアトミック操作であるため、2 つのスレッドが同時にロックしようとしても同時実行性の問題は発生しません。したがって、スレッドの 1 つが常にロックの対象となり、最終的にブロックされます。

RServer インスタンスの完全なロックが必要な場合、あなたの戦略は適切に思えます。特定の RServer インスタンス フィールドを具体的にロックできる場合は、より効率的である可能性があります。ただし、ロック操作の数が増え、より複雑になります。

于 2008-10-21T22:09:32.380 に答える
0

それは安全です。1 つのスレッドがロックを取得すると、他のスレッドはロックが解放されるまで待機する必要があります。

ただし、ロックがグローバルすぎる可能性があるため、パフォーマンスの問題が発生する可能性はありません。それはあなたの状態とそれらのスレッドによってどのように変化するかに大きく依存するので、私はそれについてあなたを助けることはできません.

于 2008-10-21T22:08:50.403 に答える
0

少し言い直すと、各 RServer インスタンスには、ロック ブロックを使用してロックされる RServerLock という変数があります。

スレッド 1 (T1) は、RServer 1 (R1) でロックを取得します。スレッド 2 (T2) が R1 を変更しようとすると、R1 ロック ブロックに入ります。この場合、T2 は T1 が終了するまで待機します。

本当に注意すべきことの 1 つは、最終的に RServer インスタンスがいくつになるかです。インスタンスが大量にある場合は、ロックするためだけに余分な 20 バイトのデータを持ち歩いていることになります。この時点で、ロック ストライピングを検討することをお勧めします。

于 2008-10-21T22:08:51.167 に答える
0

私があなたの質問を正しく理解していれば、完全に優れたホイールが既に存在するホイールを作成しようとしているように思えます。

MSDN で system.threading 名前空間を確認してください。このような状況に対処するために特別に設計された多くのロック メカニズムがあります。

http://msdn.microsoft.com/en-us/library/system.threading.aspx

于 2008-10-21T22:12:00.113 に答える