Можно ли определить, является ли операция со списком потокобезопасной?

1

Я пытаюсь выполнить сравнение для каждого элемента 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, поэтому я надеюсь извлечь из этого выгоду, если есть способ безопасно выполнить эту операцию.

  • 1
    Согласно разделу «Безопасность потоков» в списке (Of T) , да.
  • 0
    Я идиот, я присваиваю значения с помощью x.IsMatching, это влияет на ваш ответ @AndrewMorton?
Показать ещё 4 комментария
Теги:
linq
multithreading

1 ответ

4
Лучший ответ

Код, который у вас есть, является потокобезопасным. Доступ к спискам осуществляется только для чтения, а неявная синхронизация, необходимая для реализации параллелизированной версии, достаточна для обеспечения фиксации любых записей. Вы изменяете элементы в списке, но опять же, синхронизация, неявная в параллельной операции, с которой обязательно должен ждать текущий поток, гарантирует, что любые записи объектов элемента будут видны в текущем потоке.

Тем не менее, безопасность потоков не имеет значения, потому что вы все это делаете неправильно. Вы применяете алгоритм грубой силы 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 намного превосходит любой из параметров, которые у вас есть до сих пор.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню