5

列挙されたコレクションを変更しましたが、その周りにロックを配置しました...「コレクションが変更されました;列挙操作が実行されない可能性があります」というメッセージが表示される理由がわかりません。「foreach (IObserver obs in _observers.ToList())」で解決したくありません

コードはオブザーバーパターンです:

class Program
{
    static void Main(string[] args)
    {
        Subject sub = new Subject();
        Obs1 obs1 = new Obs1(sub);
        Obs2 obs2 = new Obs2(sub);

        sub.Nodefiy();
        sub.Nodefiy();
        sub.Nodefiy();
        sub.Nodefiy();
        sub.Nodefiy();
        sub.Nodefiy();

        Console.ReadKey();
    }
}

public interface IObserver
{
    void Update(int data);
}

public interface ISubscrib
{
    void Reg(IObserver obs);
    void UnReg(IObserver obs);
    void Nodefiy();
}

public class Subject : ISubscrib
{
    private static Object _lock;
    private List<IObserver> _observers;
    private int data = 0;

    public Subject()
    {
        _lock = new Object();
        _observers = new List<IObserver>();
    }

    public void Reg(IObserver obs)
    {
        lock (_lock)
        {
            _observers.Add(obs);
        }
    }

    public void UnReg(IObserver obs)
    {
        lock (_lock)
        {
            int ind = _observers.IndexOf(obs);
            if (ind >= 0)
            {
                _observers.RemoveAt(ind);
            }
        }
    }

    public void Nodefiy()
    {
        data = data + 1;
        lock (_lock)
        {
            int sentData = data;
            //foreach (IObserver obs in _observers.ToList<IObserver>())
            foreach (IObserver obs in _observers)
            {
                obs.Update(sentData);
            }
        }
    }
}

public class Obs1 : IObserver
{
    private ISubscrib _subj;
    public Obs1(ISubscrib subj)
    {
        _subj = subj;
        _subj.Reg(this);
    }

    public void Update(int data)
    {
        Console.WriteLine("Obs1: {0}", data);
    }
}

public class Obs2 : IObserver
{
    private ISubscrib _subj;

    public Obs2(ISubscrib subj)
    {
        _subj = subj;
        _subj.Reg(this);
    }

    public void Update(int data)
    {
        Console.WriteLine("Obs2: {0}", data);
        if (data > 3)
        {
            _subj.UnReg(this);
        }
    }
}

誰でも私を助けることができますか?ありがとう...

4

3 に答える 3

2

Obj2 がこの foreach ループ内で Update を呼び出すと、同じスレッドで Subject オブジェクトに戻り、この _observers コレクションを変更します。そのため、ロックが機能していません。これは同期の問題ではありません。あなたの問題は同じスレッドで発生しています。

このコードで何をしようとしているのかわからないので、これ以上お手伝いできません。

于 2012-12-15T15:10:06.717 に答える
1

問題は、_observers のリストを反復している間に、別のスレッドからコレクションを追加または変更していると思います。

削除されたオブザーバーにアクセスしようとすると、インデクサーで for を使用するような解決策でさえ役に立たないため、スレッドを同期することができます。

Mutexesを調べて、スレッドがコレクションを反復処理している間、別のスレッドが完了するまで待ってからコレクションを変更する方法を実装してみてください。

于 2012-12-15T14:44:30.980 に答える
1

ロックはスレッドの同期に関係していますが、なぜそれが役立つと思ったのかわかりません。

コレクションの変更中にコレクションを列挙することはできません (関連するすべての質問を参照してください)。代わりに foreach を次のように変更します。

for (int i = _observers.Count-1; i >= 0; i--) {
        _observers[i].Update(sentData);
}                
于 2012-12-15T13:38:08.110 に答える