Как можно оптимизировать этот код, заменив использование ConcurrentBag на List и добавив блокировку (lock) для обеспечения потокобезопасности?

Как оптимально организовать параллельное считывание данных из нескольких текстовых файлов и запись всех найденных имейлов в один файл с использованием трех потоков?
  • 24 января 2024 г. 17:08
Ответы на вопрос 2
Для начала заменим использование `ConcurrentBag<T>` на `List<T>`, добавим блокировку (`lock`) для обеспечения потокобезопасности при добавлении в общий список найденных имейлов. Это можно сделать с помощью примитива синхронизации `lock` в C#. Блок `lock` обеспечивает то, что только один поток в один момент времени может выполнить содержащийся в нем код, что предотвращает состояние гонки при доступе к общим данным.

Прежде чем оптимизировать код для параллельного считывания данных, важно знать, какие операции требуются для считывания и обработки данных. За неимением конкретного примера кода, приведу обобщенный пример использования `Parallel.ForEach` для параллельного чтения данных из нескольких файлов и записи результатов в общий список с использованием `lock`. Это предполагает, что имейлы извлекаются из текста с помощью какого-то метода `ExtractEmails`, который возвращает список имейлов, найденных в тексте файла.

```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

class Program
{
    // Общий список для хранения найденных имейлов
    static List<string> emailList = new List<string>();
    // Объект блокировки для синхронизации доступа к общему списку
    static object locker = new object();

    static void Main(string[] args)
    {
        // Массив путей к файлам
        string[] files = Directory.GetFiles("path_to_your_files_directory", "*.txt");

        // Запуск параллельной обработки файлов
        Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = 3 }, file =>
        {
            // Читаем содержимое файла
            string content = File.ReadAllText(file);
            // Извлекаем имейлы из содержимого файла
            List<string> emails = ExtractEmails(content);

            // Блокируем поток и добавляем имейлы в общий список
            lock (locker)
            {
                emailList.AddRange(emails);
            }
        });

        // Записываем все найденные имейлы в один файл
        File.WriteAllLines("path_to_output_file/emails.txt", emailList);
    }

    static List<string> ExtractEmails(string text)
    {
        // Здесь должна быть реализация извлечения имейлов из текста
        // Возвращаем список найденных имейлов
        return new List<string>();
    }
}
```

В приведенном выше коде, `Parallel.ForEach` используется для параллельного считывания и обработки файлов. Мы ограничиваем количество одновременно работающих потоков до 3, используя `ParallelOptions`.

По окончании чтения и извлечения имейлов из всех файлов происходит запись их в один файл. Это делается уже после завершения параллельного блока, чтобы убедиться, что все потоки закончили свое выполнение и общий список `emailList` содержит все найденные адреса.

Перед записью в файл можно отсортировать или иным образом обработать список имейлов, если это необходимо для вашей задачи.

Обратите внимание, что операции ввода-вывода и парсинга - это обычно более медленные операции по сравнению с многопоточным выполнением кода на CPU, так что переход от `ConcurrentBag` к `List` с `lock` может не дать ощутимого увеличения производительности, а в некоторых случаях, наоборот
Код не читал, но попробуй так: <br/> <blockquote>У меня есть задача: есть много текстовых файлов, заполненных рандомным текстом, в котором иногда встречаются имэйлы.<br/>
после того, как какой-то поток считал имэйлы, он должен записать их на один файл, при этом всех три потока должны записывать считаннные данные на этот один файл, не на разные файлы.<br/>
</blockquote> <br/> <br/> 1. Для каждого из файлов запусти по потоку (таске), каждый из которых пусть свой файл читает в поисках имеилов. <br/> 2. Для записи в итоговый файл - заведи ещё 1 поток (таску). <br/> 3. Коммуникацию между N читающими и 1 пишущим организуй через System.Threading.Channel <br/> <br/> Таким образом ты избавишься от ненужных блокировок и затрат на синхронизацию доступа к какому-то списку.
Похожие вопросы