0

ここに私のコードの断片があります。前のスレッドが終了するまで待たずに、1 つのスレッドが機能するはずです。しかし、すべてのスレッドが連続して開始され、パルス呼び出しがそれらの動作に影響を与えないことを認識しました。

class A
{
    string path = "file.xml";
    static public object _lock = new object();
    static private int accessCounter = 0;

    public List<T> GetItems()
    {
        List<T> result = null;
        lock (_lock)
        {

            while (accessCounter < 0)
                Monitor.Wait(_lock);
            accessCounter++;
            Thread.Sleep(1000);

            Monitor.Pulse(_lock);
            Thread.Sleep(1000);

            using (Stream stream = File.OpenRead(path))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(List<T>), new Type[] { typeof(T) });
                result = (List<T>)serializer.Deserialize(stream);
            }
            accessCounter--;
            Monitor.Pulse(_lock);
        }
        return result;
    }
    public void AddItem(T item)
    {
      lock(_lock){if (accessCounter!=0) Monitor.Wait(_lock);
      accessCounter = -1;
      //some writing ooperations
      accessCounter = 0;
       }
    }
}

if条件は常にtrueであるため、現在の状況では役に立たないことを知っています。さらに、それらは同時に機能するはずですが、そうではありません。

編集:このコードは、異なるスレッドから次の方法で呼び出されます:

.....
A a = new A();
var list = a.GetItems();
.....

ある種の書き込み/読み取りブロックである必要があります。したがって、スレッドが読み取りを希望し、他のスレッドが既にファイルを読み取っている場合、他のスレッドを待つ必要はありません。他のスレッドが *_lock* をキャプチャした場合、すべての読み取りスレッドを一時停止する必要があります。

4

2 に答える 2

1

アクセスカウンターのチェックが間違っています。より大きいか、より小さいかをチェックする必要があります。

while (accessCounter > 0)
    Monitor.Wait(_lock);

ただし、コードは少し奇妙に思えます。アクセスカウンターのチェックは、ロックされた重要なスレッド内で_lock行われるため、とにかく複数のスレッドがこのコードを実行することはできません。これが、コードが同時に実行されていない理由です。

また、Monitor.Waitは、現在のスレッドが保持しているロックを 解放することに注意してください。ロックが解放されるのを待機しません。

于 2012-07-24T10:15:33.673 に答える
1

フレームワークは、必要なことを実行する必要があるロックメカニズム(実際には2つ)をすでに提供しています(つまり、多数の同時リーダーを許可しますが、書き込みは排他的です(同時に他のリーダーまたはライターはありません)-ReaderWriterLockおよびReaderWriterLockSlim。

ReaderWriterLockSlimを利用するために、コードを次のように修正できます。

class A
{
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    string path = "file.xml";
    static public object _lock = new object();
    static private int accessCounter = 0;

    public List<T> GetItems()
    {
        _lock.EnterReadLock();
        try
        {
            using (Stream stream = File.OpenRead(path))
            {
                var serializer = new XmlSerializer(typeof(List<T>), new[] { typeof(T) });
                return (List<T>)serializer.Deserialize(stream);
            }
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public void AddItem(T item)
    {
        _lock.EnterWriteLock();
        try
        {
            // Some writing operations
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }
}

またはXmlSerializer以外のコンストラクターを使用して毎回新しいものを作成しているため、このメソッドはメモリリークを引き起こすことにも注意してください。これにより、毎回新しいXMLシリアル化アセンブリがビルドおよびロードされ、これらのアセンブリが解放されることはありません。上記の2つのコンストラクターのいずれかを使用するように切り替えるか、メモリをリークしないようにインスタンスをキャッシュする必要があります(おそらくによってキーが設定されます)。XmlSerializer(Type)XmlSerializer(Type, String)XmlSerializertypeof(T)

詳細については、「動的に生成されたアセンブリ」セクションを参照してください:XmlSerializerのドキュメント

于 2012-07-24T12:10:47.720 に答える