2

リストを追加/削除/変更するためにロックを使用するだけでよいと言っているのは正しいですか、それともリストを繰り返すときにロックする必要がありますか?

だから私はこれを行うことでスレッドセーフですか?

class ItemsList
{
    List<int> items = new List<int>();
    object listLock = new object();

    public void Add(int item)
    {
        lock (listLock)
        {
            items.Add(item);
        }
    }

    public void Remove(int item)
    {
        lock (listLock)
        {
            items.Remove(item);
        }
    }

    public void IncrementAll()
    {
        foreach (var item in items)
        {
            item += 1;
        }
    }
}
4

7 に答える 7

5

それを反復するときも確実にロックする必要があります-反復中にリストが変更されると、例外がスローされます。

のドキュメントからList<T>.GetEnumerator

列挙子にはコレクションへの排他的アクセス権がありません。したがって、コレクションの列挙は、本質的にスレッド セーフな手順ではありません。列挙中のスレッド セーフを保証するために、列挙全体でコレクションをロックできます。読み取りおよび書き込みのために複数のスレッドがコレクションにアクセスできるようにするには、独自の同期を実装する必要があります。

さらに、 a からの 1 回の読み取りでも、List<T>書き込みが可能な場合はスレッドセーフではありません。たとえ失敗しなくても、最新の値を取得できるという保証はありません。

基本的に、List<T>その状態がすべてのスレッドに見えるようになる最後のポイントの後に書き込まれない場合、複数のスレッドに対してのみ安全です。

スレッド セーフなコレクションが必要で、.NET 4 以降を使用している場合は、System.Collections.Concurrent名前空間を確認してください。

于 2013-01-14T15:53:34.393 に答える
2

List<T>一般に、スレッドセーフではありません。複数のリーダーを使用しても問題は発生しませんが、読み取り中にリストに書き込むことはできません。そのため、読み取りと書き込みの両方をロックするか、System.Threading.ReaderWriterLock のようなものを使用する必要があります (複数のリーダーは許可されますが、ライターは 1 つしか許可されません)。で開発している場合は、スレッド セーフなコレクションであるBlockingCollection.NET 4.0 or biggerを代わりに使用できます。

于 2013-01-14T15:57:05.737 に答える
0

複数のスレッドがコレクションを読み取ることができるように、おそらく a を使用する必要ReaderWriterLockSlimがありますが、コレクションを変更できるのは 1 つだけです。

于 2013-01-14T15:56:06.477 に答える
0

IncrementAll では、コレクションで行われた変更のために InvalidOperationException をキャッチします。次のように、テスト ユニットで確認できます。

        ItemsList il = new ItemsList();
        Task ts = new Task(() =>
        {
            for (int i = 0; i < 100000; i++)
            {
                il.Add(i);
                System.Threading.Thread.Sleep(100);

            }
        }
        );
        ts.Start();
        Task ts2 = new Task(() =>
        {
            //DoSomeActivity
            il.IncrementAll();
        }
        );
        ts2.Start();
        Console.Read();

反復もロックする必要があります!!!

于 2013-01-14T16:07:41.093 に答える
0

いいえ、それは安全ではありません。読み取り中に別のスレッドがコレクションを変更すると、「コレクションが変更されました」という種類の例外が発生します。

これを修正する最も効率的な方法は、ReaderWriterLockSlimを使用してアクセスを制御することです。これにより、複数のスレッドが同時に読み取ることができ、何かが変更を試みたときにのみロックされます。

于 2013-01-14T15:55:27.810 に答える
0

決して反復しないと、持っているものでさえスレッドセーフではありません

データ構造が意図したとおりに機能するかどうかを議論する前に、データ構造で実行している操作の種類を定義する必要があります。

ただし、一般的な場合、読み取り中はロックする必要があります。そのままでは、反復中に誰かがアイテムを追加する可能性があり、あらゆる種類のものを壊してしまいます。読み取りの途中で項目を追加すると、1 つの項目の読み取りでも壊れる可能性があります。

また、これにより、せいぜい各操作が論理的にアトミックになることに注意してください。複数の操作を実行し、データ構造の状態について推測する場合、それだけでは十分ではありません。

多くの場合、この問題を解決するには、各操作をlock.

于 2013-01-14T15:55:48.917 に答える
-1

ConcurrentQueue<>(); を見てみることをお勧めします。

これは基本的にスレッド セーフなリストであり (私の知る限り)、かなり便利です。このように少し使用できます。

   public ConcurrentQueue<yourType> alarmQueue = new ConcurrentQueue<yourType>();
    System.Timers.Timer timer;


    public QueueManager()
    {
        timer = new System.Timers.Timer(1000);
        timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
        timer.Enabled = true;
    }

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        DeQueueAlarm();
    }

    private void DeQueueAlarm()
    {
        yourType yourtype;
        while (alarmQueue.TryDequeue(out yourtype))
        {
           //dostuff
        }

    }

編集:ジョンが言ったように、これは.Net4以降で利用可能です。詳細はこちら; http://msdn.microsoft.com/en-us/library/dd267265.aspx

于 2013-01-14T16:00:35.533 に答える