Parallel.ForEach дублирует элементы в BlockingCollection
Разрабатываю текстовый поиск товаров по коллекции в памяти. Сам поиск не простой, учитывается множество вариантов написания. Есть возможность поиска нескольких товаров за раз, разделив их через запятую или другой разделитель. Поэтому, было решено распараллелить поиск при помощи Parallel.ForEach
Проблема заключается в том, что иногда результирующая коллекция возвращает дубликаты:
При первом поиске после старта приложения всё норм (скриншот слева). После второго поиска вышли дубликаты (скриншот справа)
public partial class SearchEngine<T>
{
public List<T> OriginalCollection { get; set; }
public List<T> SearchParallel(string searchText, SearchEngineOptions options)
{
if (string.IsNullOrEmpty(searchText?.Trim()))
return OriginalCollection;
//List<T> result = new List<T>();
BlockingCollection<T> result = new BlockingCollection<T>();
if (options == null)
{
options = new SearchEngineOptions()
{
Separators = new List<char>() { ',', '\n' }
};
}
//разделить строки для множественного поиска
var splitText = searchText
.Split(options.Separators.ToArray())
.Where(w => !string.IsNullOrEmpty(w?.Trim()))
.Distinct();
//установка ограничения в 20 элементов за раз
if (splitText.Length > 20)
splitText = splitText
.Take(20)
.ToArray();
if (splitText.Count() == 1)
{
return Search(searchText);
}
var parallelOptions = new ParallelOptions()
{
//MaxDegreeOfParallelism = 4
};
Parallel.ForEach(splitText, parallelOptions, (s) =>
{
var searchResult = Search(s);
foreach (var item in searchResult)
{
result.TryAdd(item);
}
});
return result.ToList();
}
public List<T> Search(string searchText)
{
//поиск товаров
}
}
Использую потокобезопасную коллекцию BlockingCollection, но увы не работает.
Платформа .NET 4.6.1
Какие еще нюансы нужно учитывать при параллельном поиске?
Ответы (1 шт):
Использую потокобезопасную коллекцию BlockingCollection, но увы не работает. У вас не работает ваша логика в коде.
- Что за текст вы в него передаете?
- Что вы делаете внутри
public List<T> Search(string searchText)? - Зачем вам вообще
BlockingCollection? - Зачем тут
Parallel.ForEach? - Что это и зачем тут это
OriginalCollection?
Ваш код можно переписать так
public partial class SearchEngine<T>
{
public List<T> OriginalCollection { get; set; }
public List<T> SearchParallel(string searchText, SearchEngineOptions options)
{
if (string.IsNullOrEmpty(searchText?.Trim()))
return OriginalCollection;
var splitText = SplitSearchText(searchText, options);
return splitText
.AsParallel()
// .WithDegreeOfParallelism(4)
.SelectMany(x=>Search(x))
.Distinct()
.ToList();
}
IEnumerable<string> SplitSearchText(string searchText, SearchEngineOptions options)
{
//разделить строки для множественного поиска
return searchText
.Split(options.Separators.ToArray(), StringSplitOptions.RemoveEmptyEntries)
.Where(w => !string.IsNullOrEmpty(w?.Trim()))
.Distinct()
.Take(20);
}
public List<T> Search(string searchText)
{
//поиск товаров
}
}
