Я пытаюсь написать программу, в которой я планирую удаление элементов, помещая их в коллекцию из разных потоков и очищая их в одном потоке, который выполняет итерацию коллекции и удаляет элементы.
Прежде чем сделать это, я задался вопросом, что обеспечит оптимальную производительность, поэтому я попробовал ConcurrentBag, ConcurrentStack и ConcurrentQueue и измерил время, необходимое для добавления 10000000 элементов.
Я использовал следующую программу, чтобы проверить это:
class Program
{
static List<int> list = new List<int>();
static ConcurrentBag<int> bag = new ConcurrentBag<int>();
static ConcurrentStack<int> stack = new ConcurrentStack<int>();
static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
static void Main(string[] args)
{
run(addList);
run(addBag);
run(addStack);
run(addQueue);
Console.ReadLine();
}
private static void addList(int obj) { lock (list) { list.Add(obj); } }
private static void addStack(int obj) { stack.Push(obj); }
private static void addQueue(int obj) { queue.Enqueue(obj); }
private static void addBag(int obj) { bag.Add(obj); }
private static void run(Action<int> action)
{
Stopwatch stopwatch = Stopwatch.StartNew();
Parallel.For(0, 10000000, new ParallelOptions() { MaxDegreeOfParallelism = # }, action);
stopwatch.Stop();
Console.WriteLine(action.Method.Name + " takes " + stopwatch.Elapsed);
}
}
где # — количество используемых потоков.
но результаты довольно запутанны:
с 8 нитями:
- addList занимает 00:00:00.8166816
- addBag занимает 00:00:01.0368712
- addStack занимает 00:00:01.0902852
- addQueue занимает 00:00:00.6555039
с 1 нитью:
- addList занимает 00:00:00.3880958
- addBag занимает 00:00:01.5850249
- addStack занимает 00:00:01.2764924
- addQueue занимает 00:00:00.4409501
поэтому, независимо от того, сколько потоков, кажется, что простая блокировка простого старого списка быстрее, чем использование любой из параллельных коллекций, за исключением, возможно, очереди, если ей нужно обрабатывать много записей.
РЕДАКТИРОВАТЬ: после комментариев ниже о сборке мусора и отладки: да, это влияет на тест. Влияние сборки отладки будет линейным, Мусор будет увеличиваться с увеличением использования памяти.
Однако выполнение одного и того же теста несколько раз дает примерно одинаковые результаты.
Я переместил инициализацию коллекции прямо перед запуском теста и теперь собираю мусор после запуска, вот так:
list = new List<int>();
run(addList);
list = null;
GC.Collect();
с MaxDegreeOfParallelism, установленным на 8, я получаю следующие результаты:
- addList занимает 00:00:7959546
- addBag занимает 00:00:01.08023823
- addStack занимает 00:00:01.1354566
- addQueue занимает 00:00:00.6597145
с отклонением 0,02 секунды каждый раз, когда я запускаю код.