У меня есть служба извлечения данных, через которую мое приложение C# извлекает данные. Данные извлекаются с использованием нескольких заданий, и после завершения запроса данных служба извлечения данных вызывает метод notify, который я реализовал в своем классе приложения.
Ниже приведен код метода notify. Он просто проверяет, не являются ли результаты пустыми, а затем вызывает mergeResults в новом потоке.
public override void notify(List<IFields> results)
{
if (!results.IsNullOrEmpty())
{
Task.Run(() => { mergeResults(results); });
}
}
Я использую список для хранения окончательных результатов слияния.
List<IFields> mergedResults;
Я использую object mergeLock для взаимного исключения.
Вот логика слияния, которую я использую:
public void mergeResults(List<IFieldsByPrePost> results)
{
lock (mergeLock)
{
foreach (var result in results)
{
if (mergedResults.Count > 0)
{
var properties = mergedResults.First().getDiffProperties();
bool isMatch = false;
foreach (var mergedResult in mergedResults)
{
isMatch = true;
foreach (var property in properties)
{
var value1 = mergedResult.GetType().GetProperty(property).GetValue(mergedResult).ToString();
var value2 = result.GetType().GetProperty(property).GetValue(result).ToString();
if (value1 != value2) { isMatch = false; break; }
}
if (isMatch)
{
mergedResult.Count += result.Count;
break;
}
}
if (!isMatch)
{
mergedResults.Add(result);
}
}
else
{
mergedResults.Add(result);
}
}
}
}
Вышеупомянутая логика работает, но очень медленно, когда в метод передается большой набор результатов.
Кроме того, метод notify вызывается несколько раз службой извлечения данных с разными наборами результатов, что еще больше замедляет его работу.
Я ищу лучший подход к решению этой проблемы.
TL; DR; Этот алгоритм медленный, может ли кто-нибудь показать мне, как заставить его работать быстрее?
Сначала вам нужно использовать точки останова, чтобы увидеть, какая строка медленная, а затем воссоздать эту функцию или использовать другую.
Также было бы здорово, если бы вы могли быть более конкретными, чем very slow и large set of results.
Вы рассматривали возможность использования Parallel.ForEach вместо петли for?
Я почти уверен, что подход отражения - одна из причин вашей проблемы с производительностью. Зачем тебе это нужно? lock (mergeLock) в начале длительной операции блокирует всю программу, поэтому Task.Run кажется бесполезным.
@TimSchmelter - Мы не можем улучшить службу извлечения данных. Это что-то вне нашего контроля. Таким образом, метод уведомления будет вызван всеми способами, которые будут продолжать порождать новые потоки и вызовут метод mergeResults. Но поскольку блокировка используется в самом начале метода, одновременно разрешается только один поток.
@TimSchmelter - Вы правы в подходе к отражению. Я провел тест профилировщика производительности и увидел, что 49% циклов ЦП тратятся только на вызовы методов getProperty () и getValue (). Набор результатов, который мы получаем, имеет абстрактный тип и может иметь любой из 10 различных конкретных объектов класса. Вызов getDiffProperties () выполняется для получения отличительных свойств от этого класса для выполнения слияния.
@TimSchmelter - Да, верно и в отношении Task.Run (), я ищу подход для выполнения этого метода в многопоточном режиме, но не хочу, чтобы несколько потоков вызывали проблему чтения-записи.
Является ли вместо этой штуки getDiffProperties(); альтернативой написание функции наподобие IsMatch(IFieldsByPrePost post){...} в вашем классе IFieldsByPrePost?





Я бы предположил, что IFields и / или IFieldsByPrePost являются производными от
IEquatable<IFields> and/or IEquatable<IFieldsByPrePost>.
Таким образом, вы можете просто проверить равенство с помощью
IFields fields1;
IFieldsByPrePost fields2;
bool equal = fields1.Equals(fields2);
Таким образом вы обойдете отражение, которое замедляет ваш код. Тогда это просто
foreach (var result in results)
{
if (!mergedResults.Any(x => x.Equals(result))
{
mergedResults.Add(result);
}
}
Я не знаю, что ты делаешь с
mergedResult.Count,
поэтому я опускаю это.
Спасибо за вклад в IEquatable. Я попробую и опубликую результат.
конечно, тогда вам придется сравнивать элементы в методе Equals (other). И если вы просто поместите туда свой код отражения, это не поможет. Но там у вас должен быть доступ ко всем полям (даже частным полям того же класса). В зависимости от количества классов, реализующих IFields, это может потребовать некоторой работы.
Оно работало завораживающе. Когда все потоки завершают извлечение данных, одновременно завершается и слияние. Это просто исключило 3-й цикл из кода. Благодаря тонну.
Подумайте о том, чтобы сделать mergedResults HashSet и использовать Contains вместо Any для повышения производительности для больших наборов.
В первую очередь меня бросает в глаза то, что метод mergeResults не является универсальным, поэтому я не уверен, зачем нужна рефлексия. Удаление строк:
var value1 = mergedResult.GetType().GetProperty(property).GetValue(mergedResult).ToString();
var value2 = result.GetType().GetProperty(property).GetValue(result).ToString();
if (value1 != value2) { isMatch = false; break; }
и используя прямое свойство:
if (mergedResult.Property1 == result.Property1) { isMatch = false; break; }
может помочь.
Было бы замечательно, если бы вы могли предоставить минимальный воспроизводимый пример с образцами входных данных и ожидаемыми результатами, основанными на этих образцах входных данных.