Я пытаюсь выполнить сравнение для каждого элемента X в списке ListA, если два свойства X, X.Code и X.Rate соответствуют коду и скорости любого элемента Y в ListB. Текущее решение использует LINQ и AsParallel для выполнения этих сравнений (время - это фактор, и каждый список может содержать от 0 элементов до пары сотен элементов каждый).
Пока метод AsParallel выглядит намного быстрее, однако я не уверен, что эти операции являются потокобезопасными. Я понимаю, что, поскольку это сравнение будет только считывать значения и не изменять их, чтобы это было безопасно, но я не уверен на 100%. Как определить, является ли эта операция безопасным потоком, прежде чем развязывать ее в моей производственной среде?
Вот код, с которым я работаю:
var s1 = System.Diagnostics.Stopwatch.StartNew();
ListA.AsParallel().ForAll(x => x.IsMatching = ListB.AsParallel().Any(y => x.Code== y.Code && x.Rate== y.Rate));
s1.Stop();
var s2 = System.Diagnostics.Stopwatch.StartNew();
ListA.ForEach(x => x.IsMatching = ListB.Any(y => x.Code == y.Code && x.Rate== y.Rate));
s2.Stop();
В настоящее время каждый метод возвращает тот же результат, однако AsParallel() выполняет в ~ 1/3 время как обычный ForEach, поэтому я надеюсь извлечь из этого выгоду, если есть способ безопасно выполнить эту операцию.
Код, который у вас есть, является потокобезопасным. Доступ к спискам осуществляется только для чтения, а неявная синхронизация, необходимая для реализации параллелизированной версии, достаточна для обеспечения фиксации любых записей. Вы изменяете элементы в списке, но опять же, синхронизация, неявная в параллельной операции, с которой обязательно должен ждать текущий поток, гарантирует, что любые записи объектов элемента будут видны в текущем потоке.
Тем не менее, безопасность потоков не имеет значения, потому что вы все это делаете неправильно. Вы применяете алгоритм грубой силы O (N ^ 2) к необходимости, которую можно решить с помощью более элегантного и эффективного решения, LINQ join
:
var join = from x in list1
join y in list2 on new { x.Code, x.Rate } equals new { y.Code, y.Rate }
select x;
foreach (A a in join)
{
a.IsMatching = true;
}
В вашем примере кода не было инициализации выборочных данных. Поэтому я не могу воспроизвести ваши результаты с любой надежностью. В самом деле, в моем тестовом наборе, где я инициализировал list1
и list2
тождественно, каждый из которых имеет одинаковые 1000 элементов (я просто устанавливаю Code
и Rate
в индекс элемента в списке, то есть от 0
до 999
), я нашел AsParallel()
медленнее, чем серийная версия, чуть более 25% (т.е. 250 итераций параллельной версии заняли около 2,7 секунды, а 250 итераций серийной версии заняли около 1,9 секунды).
Но ни одна из них не приблизилась к версии join
, которая завершила 250 итераций этих конкретных тестовых данных примерно за 60 миллисекунд, что почти в 20 раз быстрее, чем более быстрые из двух других реализаций.
Я уверен, что, несмотря на отсутствие сопоставимого набора данных по сравнению с вашим сценарием, основной результат будет по-прежнему стоять, и вы поймете, что использование подхода join
намного превосходит любой из параметров, которые у вас есть до сих пор.