Почему параллельный класс не останавливается при вызове .Break()?

#c# #parallel.foreach

#c# #parallel.foreach

Вопрос:

Мне нужно выполнить поиск подстроки в более чем 10k файлах и вернуть первое совпадение. Для этого я написал этот метод, используя Parallel класс:

 public class FileSearchResult
{
    public long Id { get; }
    public string FilePath { get; }
    public IList<SearchMatch> Matches { get; }

    public FileSearchResult(long id, string filePath)
    {
        Id = id;
        FilePath = filePath;
        Matches = new List<SearchMatch>();
    }
}

public class SearchMatch
{
    public int Index { get; set; }
    public int Length { get; set; }
}

public FileSearchResult[] SearchInFiles(string query, string[] filePathList)
{
    if (string.IsNullOrWhiteSpace(query) || filePathList == null || filePathList.Length == 0)
    {
        return Array.Empty<FileSearchResult>();
    }

    Regex regex = new Regex(query, RegexOptions.IgnoreCase | RegexOptions.Multiline);
    ConcurrentBag<FileSearchResult> matchesList = new ConcurrentBag<FileSearchResult>();

    Parallel
        .ForEach
        (
            filePathList,
            (file, state, ix) =>
            {
                if (state.ShouldExitCurrentIteration)
                {
                    return;
                }

                string fileContent = File.ReadAllText(file);
                var results = regex.Matches(fileContent);

                if (results.Count == 0)
                {
                    return;
                }

                state.Break();

                //if(state.LowestBreakIteration.HasValue)
                //{
                //    Interlocked.Exchange(ref lowestBreakIteration, state.LowestBreakIteration.Value);
                //}

                FileSearchResult result = new FileSearchResult(ix, file);

                for (int i = 0; i < results.Count;   i)
                {
                    if (state.ShouldExitCurrentIteration)
                    {
                        return;
                    }

                    var regexMatch = results[i];
                    SearchMatch match = new SearchMatch();
                    match.Index = regexMatch.Index;
                    match.Length = regexMatch.Length;
                    result.Matches.Add(match);
                }

                if (state.ShouldExitCurrentIteration)
                {
                    return;
                }

                matchesList.Add(result);
            }
        );

    return matchesList.ToArray();
}

public FileSearchResult[] SearchInFiles(string query, string sourceFolderPath)
{
    if (string.IsNullOrWhiteSpace(sourceFolderPath) || !Directory.Exists(sourceFolderPath))
    {
        return Array.Empty<FileSearchResult>();
    }

    string[] filePathList = Directory.GetFiles(sourceFolderPath);
    return SearchInFiles(query, filePathList);
}
  

В принципе, это работает, но если я ищу слово, которое есть во всех файлах, этот метод должен возвращать только 1-й найденный элемент, вместо этого я получаю 2 или иногда 3 элемента.
Если я отлаживаю вызов state.Break(); , я вижу, что иногда он вызывается более одного раза.
Что я делаю не так?

Комментарии:

1. Я бы сказал, что вы не делаете ничего неправильного, это ожидаемо. Посмотрите на learn.microsoft.com/en-us/previous-versions/dd460721 (v=против110) . Возможно, вместо этого используйте stop …

2. Я ожидал, что ParallelLoopState.ShouldExitCurrentIteration это будет true после любого .Break() вызова, но я вижу, что you cannot control whether other threads on a loop continue to run after either Stop or Break is called.