IT

List. 스레드 안전 추가()

itgroup 2023. 5. 23. 21:47
반응형

List. 스레드 안전 추가()

일반적으로 목록이 스레드 안전하지 않다는 것은 이해하지만, 스레드가 목록에서 다른 작업(예: 트래버스)을 수행하지 않는 경우 목록에 항목을 단순히 추가하는 것이 잘못된 것입니까?

예:

List<object> list = new List<object>();
Parallel.ForEach(transactions, tran =>
{
    list.Add(new object());
});

백그라운드에서 버퍼 재할당 및 요소 복사 등 많은 작업이 수행됩니다.그 코드는 위험을 야기할 것입니다.간단히 말해서, 목록에 추가할 때 원자적 작업이 없으며, 적어도 "길이" 속성은 업데이트되어야 하며, 항목은 올바른 위치에 배치되어야 하며, (별도 변수가 있는 경우) 인덱스를 업데이트해야 합니다.여러 스레드가 서로를 짓밟을 수 있습니다.그리고 성장이 필요하다면 더 많은 일들이 진행되고 있습니다.어떤 것이 목록에 글을 쓰고 있다면 다른 어떤 것도 읽거나 글을 쓰면 안 됩니다.

.NET 4.0에서는 스레드 안전하고 잠금이 필요 없는 동시 컬렉션이 있습니다.

현재의 접근 방식은 스레드 세이프가 아닙니다. 기본적으로 데이터 변환을 수행하기 때문에 PLINQ가 더 나은 접근 방식일 수 있습니다(간단한 예이지만 결국에는 각 트랜잭션을 다른 "상태" 개체로 투영합니다).

List<object> list = transactions.AsParallel()
                                .Select( tran => new object())
                                .ToList();

다음과 같은 방법으로 문제를 해결했습니다.

ConcurrentBag<object> list = new ConcurrentBag<object>();
Parallel.ForEach(transactions, tran =>
{
    list.Add(new object());
});

당신이 경우용을 사용하고 .List.add여러 스레드에서 주문에 대해 신경쓰지 않고, 그러면 아마도 당신은 색인화 기능이 필요하지 않을 것입니다.List사용 가능한 동시 컬렉션 중 일부를 대신 사용해야 합니다.

만약 당신이 이 충고를 무시하고 단지 한다면,add당신은 만들 수 있습니다.add스레드는 안전하지만 다음과 같이 예측할 수 없는 순서로:

private Object someListLock = new Object(); // only once

...

lock (someListLock)
{
    someList.Add(item);
}

이 예측 한 순서를 , 을 할 수 하지 않을 가능성이 .someList[i].

그것은 무리한 부탁이 아닙니다.다른 방법과 함께 스레드 안전 문제를 일으킬 수 있는 방법이 호출된 유일한 방법인 경우 안전한 경우가 있습니다.

그러나 리플렉터에 표시된 코드를 고려할 때 이는 분명히 해당되지 않습니다.

public void Add(T item)
{
    if (this._size == this._items.Length)
    {
        this.EnsureCapacity(this._size + 1);
    }
    this._items[this._size++] = item;
    this._version++;
}

라 할지라도EnsureCapacity그 자체로 스레드 세이프(그리고 그것은 매우 확실하지 않음)였으며, 증분 연산자에 대한 동시 호출이 오서링을 유발할 가능성을 고려할 때 위의 코드는 스레드 세이프가 아닐 것이 분명합니다.

잠금, ConcurrentList 사용 또는 여러 스레드가 작업을 마친 후 여러 스레드가 쓰는 장소에서 잠금이 없는 대기열을 사용하여 직접 또는 목록을 채우는 방식으로 읽습니다(여기서는 단일 스레드 읽기에 이은 여러 동시 쓰기가 패턴이라고 가정합니다).그렇지 않다면, 나는 그 상황이 어떤지 볼 수 없습니다.Add는 호출된 유일한 방법입니다.)

이렇게 하면 목록이 배열 위에 작성되어 스레드 안전하지 않기 때문에 스레드 위치에 따라 인덱스 범위를 벗어나는 예외가 발생하거나 다른 값을 재정의하는 일부 값이 발생할 수 있습니다.기본적으로, 하지 마세요.

여러 가지 잠재적인 문제가 있습니다.그냥 하지 마세요.스레드 세이프 컬렉션이 필요한 경우 잠금 장치를 사용하거나 시스템 중 하나를 사용합니다.컬렉션.동시 컬렉션입니다.

스레드가 목록에서 다른 작업을 수행하지 않는 경우 단순히 항목을 목록에 추가하는 것이 잘못된 것입니까?

간단한 대답: 네.

긴 답변: 아래 프로그램을 실행합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

class Program
{
    readonly List<int> l = new List<int>();
    const int amount = 1000;
    int toFinish = amount;
    readonly AutoResetEvent are = new AutoResetEvent(false);

    static void Main()
    {
        new Program().Run();
    }

    void Run()
    {
        for (int i = 0; i < amount; i++)
            new Thread(AddTol).Start(i);

        are.WaitOne();

        if (l.Count != amount ||
            l.Distinct().Count() != amount ||
            l.Min() < 0 ||
            l.Max() >= amount)
            throw new Exception("omg corrupted data");

        Console.WriteLine("All good");
        Console.ReadKey();
    }

    void AddTol(object o)
    {
        // uncomment to fix
        // lock (l) 
        l.Add((int)o);

        int i = Interlocked.Decrement(ref toFinish);

        if (i == 0)
            are.Set();
    }
}

다른 사람들이 이미 말했듯이, 당신은 당신은 당신의 웹사이트에서 동시 수집을 사용할 수 있습니다.System.Collections.Concurrent네임스페이스입니다.이 중 하나를 사용할 수 있는 경우 이 방법이 좋습니다.

하지만 만약 당신이 단지 동기화된 목록을 정말 원한다면, 당신은 그것을 볼 수 있습니다.SynchronizedCollection<T>-클래스 인System.Collections.Generic.

시스템을 포함해야 했습니다.서비스 모델 어셈블리, 이것이 제가 그것을 그렇게 좋아하지 않는 이유이기도 합니다.하지만 가끔 사용합니다.

다른 스레드에 요소를 추가하는 것도 스레드 안전하지 않습니다.

C# 4.0에는 동시 컬렉션이 있습니다(http://jiezhu0815.blogspot.com/2010/08/c-40-feature-1-concurrent-collections.html) 참조).

언급URL : https://stackoverflow.com/questions/5589315/list-add-thread-safety

반응형