ConcurrentBag <MyType> Vs List <MyType>
В чем преимущество использования ConcurrentBag (Of MyType) против использования List (Of MyType)?
На странице MSDN на CB указано, что
ConcurrentBag (Of T) - это поточно-безопасный мешок, оптимизированный для сценарии, в которых тот же поток будет быть как производством, так и потреблением данных хранящиеся в сумке
Так в чем же преимущество? Я могу понять преимущества других типов коллекций в пространстве имен Concurrency, но это меня озадачило.
Ответы
Ответ 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.