3

重複の可能性:
C# のスレッド化とブロッキング

どちらのアプローチが優れているかを効果的に判断しようとしています。

現在、遅延読み込み方式で読み込まれるエンティティを公開するシングルトン インスタンスがあります。それぞれにいくつかの利点がある 3 つのアプローチをリストしました。最初のアプローチは、スレッドの安全性を確保するために二重ロック パターンのみに依存します。2 番目のアプローチはロックを使用しませんが、レースの場合に二重ロードの可能性があります。3 番目のアプローチは、私が非常に気に入っているソリューションを実際に使用しています。(System.Lazy)。

何らかの理由で、2 番目のアプローチ (System.Thread.InterLocked) に何か問題があると感じていますが、それを特定することはできません。あるアプローチを他のアプローチよりも優先する理由はありますか? これについては、以前の投稿で取り上げましたが、今後は 3 番目のオプションが進むべき道だと感じました。

設計を説明できるように、コードを最小限に抑えました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TPLDemo
{
  public class SomeEntity
  {
  }

  public class MultiThreadedManager
  {
    private static readonly System.Lazy<MultiThreadedManager> instance = new Lazy<MultiThreadedManager>(() => { return new MultiThreadedManager(); });
    private readonly object _syncRoot = new object();
    private List<SomeEntity> _inMemoryEntities = null;
    private List<SomeEntity> _inMemoryEntitiesUsingLockFreeApproach = null;
    private System.Lazy<List<SomeEntity>> _inMemoryUsingLazy = new Lazy<List<SomeEntity>>(() => { return MultiThreadedManager.Instance.LoadFromSomewhere(); });

    public static MultiThreadedManager Instance
    {
      get { return instance.Value; }
    }


    public IEnumerable<SomeEntity> LazyEntities
    {
      get
      {
        return _inMemoryUsingLazy.Value;
      }
    }

    public IEnumerable<SomeEntity> LocklessEntities
    {
      get
      {
        if (_inMemoryEntitiesUsingLockFreeApproach == null)
        {
          do
          {
          // Is it possible multiple threads hit this at the same time?
          } while (System.Threading.Interlocked.CompareExchange<List<SomeEntity>>(ref                _inMemoryEntitiesUsingLockFreeApproach, this.LoadFromSomewhere(), null) != null);
        }

        return _inMemoryEntitiesUsingLockFreeApproach;
      }
    }


    /// <summary>
    /// This is thread safe but it involved some locking. 
    /// </summary>
    public IEnumerable<SomeEntity> Entities
    {
      get
      {
        if (_inMemoryEntities == null)
        {
          lock (_syncRoot)
          {
            if (_inMemoryEntities == null)
            {
              List<SomeEntity> list = this.LoadFromSomewhere();
              _inMemoryEntities = list;
            }
          }
        }

        return _inMemoryEntities;
      }
    }

    private List<SomeEntity> LoadFromSomewhere()
    {
      return new List<SomeEntity>();
    }

    public void ReloadEntities()
    {
      // This is sufficient becasue any subsequent call will reload them safely. 
      _inMemoryEntities = null;

      // This is sufficient becasue any subsequent call will reload them safely. 
      _inMemoryEntitiesUsingLockFreeApproach = null;

      // This is necessary becasue _inMemoryUsingLazy.Value is readonly.
      _inMemoryUsingLazy = new Lazy<List<SomeEntity>>(() => { return MultiThreadedManager.Instance.LoadFromSomewhere(); });
    }
  }
}
4

1 に答える 1

0

3 番目のオプション ( Lazy) では、動作方法を構成できます。(1) または (2) のように動作させることができます。

いずれにせよ、一度ロードされると、ロードされた を戻すために内部でロックまたはインターロックする必要はありませんValue

だからぜひ行ってSystem.Lazyください。

Valueただし、厄介な点が 1 つあります。ファクトリ関数が失敗すると、例外が格納され、プロパティにアクセスするたびにスローされます。これは、このLazyインスタンスが破棄されていないことを意味します。再試行することはできません。これは、一時的な障害 (ネットワーク エラーなど) により、手動で再起動するまでアプリケーションが完全に停止する可能性があることを意味します。

MS Connect でこれについて不平を言っている場合、それは仕様によるものです。

私の解決策は、自分で書くことでしたLazy。それは難しいことではありません。

于 2012-10-12T00:01:54.870 に答える