1

ファイルの状態情報を保持するために使用されるスレッド間で共有オブジェクトがあります。情報を保持するオブジェクトは次のクラスです。

/// <summary>
/// A synchronized dictionary class.
/// Uses ReaderWriterLockSlim to handle locking. The dictionary does not allow recursion by enumeration. It is purly used for quick read access.
/// </summary>
/// <typeparam name="T">Type that is going to be kept.</typeparam>
public sealed class SynchronizedDictionary<U,T> : IEnumerable<T>
{
    private System.Threading.ReaderWriterLockSlim _lock = new System.Threading.ReaderWriterLockSlim();
    private Dictionary<U, T> _collection = null;

    public SynchronizedDictionary()
    {
        _collection = new Dictionary<U, T>();
    }

    /// <summary>
    /// if getting:
    /// Enters read lock.
    /// Tries to get the value.
    /// 
    /// if setting:
    /// Enters write lock.
    /// Tries to set value.
    /// </summary>
    /// <param name="key">The key to fetch the value with.</param>
    /// <returns>Object of T</returns>
    public T this[U key]
    { 
        get
        {
            _lock.EnterReadLock();
            try
            {
                return _collection[key];
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        set
        {
            Add(key, value);
        }

    }

    /// <summary>
    /// Enters write lock. 
    /// Removes key from collection
    /// </summary>
    /// <param name="key">Key to remove.</param>
    public void Remove(U key)
    {
        _lock.EnterWriteLock();
        try
        {
            _collection.Remove(key);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    /// <summary>
    /// Enters write lock.
    /// Adds value to the collection if key does not exists.
    /// </summary>
    /// <param name="key">Key to add.</param>
    /// <param name="value">Value to add.</param>
    private void Add(U key, T value)
    {
        _lock.EnterWriteLock();
        if (!_collection.ContainsKey(key))
        {
            try
            {
                _collection[key] = value;
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

    }

    /// <summary>
    /// Collection does not support iteration.
    /// </summary>
    /// <returns>Throw NotSupportedException</returns>
    public IEnumerator<T> GetEnumerator()
    {
        throw new NotSupportedException();
    }

    /// <summary>
    /// Collection does not support iteration.
    /// </summary>
    /// <returns>Throw NotSupportedException</returns>
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotSupportedException();
    }

}

私はこの辞書を次のように呼んでいます。SynchronizedDictionary_cache=new SynchronizedDictionary();

他のスレッドを生成して、次のようにスレッドを使用できます。_cache ["key"];

辞書は実行時に変更できます。ここでは問題ありません。それとも私は間違っていますか?私の目には、コレクションを反復処理する列挙子を作成したいので、問題は列挙子にあります。どうすればよいですか?私はこれらの3つの解決策を考えました:

  1. 次のような列挙子を作成します:http: //www.codeproject.com/Articles/56575/Thread-safe-enumeration-in-C (ただし、ReaderWriterLockSlimを使用)
  2. SyncRootと同様に(ただしReaderWriterLockSlimを使用して)ロックオブジェクトを公開し、呼び出し元がenterメソッドとexit読み取りメソッドを呼び出すようにします。
  3. 代わりに、情報を保持するデータベース(SQLite fx)を使用してください。

番号1)の問題は次のとおりです。

  1. コンストラクターを使用して読み取りモードに入ります。GetEnumerator()がforeachを使用せずに手動で呼び出された場合はどうなりますか?そして、disposeを呼び出すのを忘れてください。
  2. これが良いコーディングスタイルかどうかはわかりません。私はコードが好きですが。
  3. 呼び出し元がforeachを使用している場合、列挙子のインスタンス化と破棄の呼び出しの間に呼び出し元が何をする可能性があるのか​​わかりません。私が正しく読んだドキュメントを理解していれば、重い作業をしているリーダーが1人残っている限り、これはライターをブロックしてしまう可能性があります。

2)の問題は次のとおりです。

  1. 私はこれを公開するのが好きではありません。.NET APIがそれを行うことは知っていますが、好きではありません。
  2. 適切に出入りするのは発信者次第です

3)目は問題ありません。しかし、私はこの小さなプロジェクトを空き時間プロジェクトとして行っており、マルチスレッドとリフレクションについてもっと学びたいので、これを最後のオプションとして残しておきたいと思います。実行時にコレクションを反復処理する理由は、いくつかの基準に一致する値を見つけたいからです。

たぶん、問題を発明したのは私だけですか?

ConcurrentDictionaryを知っていますが、これは使いたくありません。私はこのプロジェクトを遊び場として使っています。糸脱毛と反射で遊ぶ。

編集

読み書きをしているのは何なのかと聞かれました。そして、私はこの編集でこれを伝えるつもりです。私はこのクラスを読んだり書いたりしています:

public class AssemblyInformation
{
    public string FilePath { get; private set; }
    public string Name { get; private set; }

    public AssemblyInformation(string filePath, string name)
    {
        FilePath = filePath;
        Name = name;
    }
}

私はたくさんの読み取りを行っていますが、実行時の書き込みはほとんどありません。多分私は2000年と1回の書き込みを行います。たくさんのオブジェクトもありません、多分200。

4

2 に答える 2

2

私はあなたの質問をあなたが学ぶのを助けるフィードバックの要求として扱います。すでに特定した3つのソリューションについて説明します。

  1. はい、そのため、このようなデザインをAPIとしてサードパーティ(または他の開発者)に公開してはなりません。正しく使用するのは難しいです。このcodeprojectの記事には、いくつかの厄介なアドバイスがあります。
  2. このモデルは、暗黙的ではなく、ロックについて明示的であるため、はるかに優れています。しかし、これは私の意見では関心の分離に違反します。
  3. ここで何を意味するのかわかりません。辞書にSnapshot()メソッドを設定して、安全に渡して読み取ることができる読み取り専用のコピーを作成することができます。これは、ソリューション1とは異なるトレードオフです。

まったく別の解決策があります。不変の辞書を使用します。このような辞書は、同時書き込みアクセス下でも安全に受け渡され、読み取られ、列挙される可能性があります。このような辞書/マップは通常、ツリーを使用して実装されます。

重要なポイントについて詳しく説明します。並行システム全体について考える必要があります。すべてのコンポーネントをスレッドセーフ(この場合は辞書)にすることで、アプリを正しくすることはできません。辞書を何に使用しているかを定義する必要があります。

あなたは言う:

実行時にコレクションを反復処理する理由は、いくつかの基準に一致する値を見つけたいからです。

データに対して同時書き込みが発生していて、辞書からアトミックに一貫性のあるスナップショットを取得したい場合(UIで進行状況レポートを撮影する場合がありますか?)。この目標がわかったので、解決策を考案できます。

読み取りロックを取得しながらすべてのデータを複製するCloneメソッドを辞書に追加できます。これにより、呼び出し元に新しいオブジェクトが提供され、それを個別に列挙できます。これは、クリーンで安全に公開できるAPIになります。

于 2012-03-24T22:40:50.527 に答える
2

直接実装する代わりに、 (のような)プロパティIEnumerableを追加します。ValuesDictionary.Values

public IEnumerable<T> Values {
  get {
    _lock.EnterReadLock();
    try {
      foreach (T v in _collection.Values) {   
        yield return v;
      }
    } finally {
      _lock.ExitReadLock();
    }
  }
}
于 2012-03-24T22:44:57.227 に答える