Подтвердить что ты не робот

ConcurrentBag <MyType> Vs List <MyType>

В чем преимущество использования ConcurrentBag (Of ​​MyType) против использования List (Of MyType)? На странице MSDN на CB указано, что

ConcurrentBag (Of ​​T) - это поточно-безопасный мешок, оптимизированный для сценарии, в которых тот же поток будет быть как производством, так и потреблением данных хранящиеся в сумке

Так в чем же преимущество? Я могу понять преимущества других типов коллекций в пространстве имен Concurrency, но это меня озадачило.

4b9b3361

Ответ 1

Внутренне ConcurrentBag реализуется с использованием нескольких разных списков, по одному для каждого потока писем.

То, что вы указываете, означает, что при чтении из сумки он будет расставлять приоритеты в списке, созданном для этого потока. Смысл, он сначала проверит список для этого потока, прежде чем рискнуть соперничеством в другом списке потоков.

Таким образом, он может минимизировать конфликт блокировки, когда несколько потоков одновременно считывают и записывают. Когда поток чтения не имеет списка или его список пуст, он должен заблокировать список, назначенный для другого потока. Но, если у вас есть несколько потоков, которые все читают и записывают в свой собственный список, тогда у вас никогда не будет конкуренции за блокировку.

Ответ 2

Самое большое преимущество в том, что ConcurrentBag<T> безопасен для доступа из нескольких потоков, а LisT<T> - нет. Если для вашего сценария важно обеспечить безопасный доступ к потоку, то тип ConcurrentBag<T>, возможно, в ваших интересах превышает LisT<T> + ручную блокировку. Нам нужно узнать немного больше о вашем сценарии, прежде чем мы сможем ответить на этот вопрос.

Дополнительно LisT<T> - упорядоченная коллекция, а ConcurrentBag<T> - нет.

Ответ 3

В отличие от других параллельных коллекций, ConcurrentBag<T> оптимизирован для однопоточного использования.
В отличие от List<T>, ConcurrentBag<T> может использоваться одновременно из нескольких потоков.

Ответ 4

Я думаю, вы должны прочитать, что "когда несколько потоков обращаются к контейнеру, и каждый поток может производить и/или потреблять данные", он определенно предназначен для параллельных сценариев.

Ответ 5

TL;DR; Я бы сказал, что локальный замок быстрее, но разница незначительна. (или я поднял настройку своего теста).

Анализ производительности:

private static IEnumerable<string> UseConcurrentBag(int count)
    {
        Func<string> getString = () => "42";

        var list = new ConcurrentBag<string>();
        Parallel.For(0, count, o => list.Add(getString()));
        return list;
    }

    private static IEnumerable<string> UseLocalLock(int count)
    {
        Func<string> getString = () => "42";
        var resultCollection = new List<string>();
        object localLockObject = new object();
        Parallel.For(0, count, () => new List<string>(), (word, state, localList) =>
        {
            localList.Add(getString());
            return localList;
        },
            (finalResult) => { lock (localLockObject) resultCollection.AddRange(finalResult); }
            );

        return resultCollection;
    }

    private static void Test()
    {
        var s = string.Empty;
        var start1 = DateTime.Now;
        var list = UseConcurrentBag(5000000);
        if (list != null)
        {
            var end1 = DateTime.Now;
            s += " 1: " + end1.Subtract(start1);
        }

        var start2 = DateTime.Now;
        var list1 = UseLocalLock(5000000);
        if (list1 != null)
        {
            var end2 = DateTime.Now;
            s += " 2: " + end2.Subtract(start2);
        }

        if (!s.Contains("sdfsd"))
        {
        }
    }

Предел ошибки с использованием ConcurrentBag, выполняющегося 3 раза против самого себя с помощью записей 5M

"1: 00: 00: 00.4550455 2: 00: 00: 00.4090409"
"1: 00: 00: 00.4190419 2: 00: 00: 00.4730473"
"1: 00: 00: 00.4780478 2: 00: 00: 00.3870387"

3 запускает ConcurrentBag против локальной блокировки с 5M-записями:

"1: 00: 00: 00.5070507 2: 00: 00: 00.3660366"
"1: 00: 00: 00.4470447 2: 00: 00: 00.2470247"
"1: 00: 00: 00.4420442 2: 00: 00: 00.2430243"

С 50M записями

"1: 00: 00: 04.7354735 2: 00: 00: 04.7554755"
"1: 00: 00: 04.2094209 2: 00: 00: 03.2413241"

Я бы сказал, что локальный замок немного быстрее

UPDATE: Вкл. (Xeon X5650 @2,67 ГГц 64-бит Win7 6 ядро) "локальная блокировка", похоже, работает еще лучше

С 50 М записей.

1: 00: 00: 09.7739773 2: 00: 00: 06.8076807
1: 00: 00: 08.8858885 2: 00: 00: 04.6184618
1: 00: 00: 12.5532552 2: 00: 00: 06.4866486.