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


1. Для каждого из файлов запусти по потоку (таске), каждый из которых пусть свой файл читает в поисках имеилов.
2. Для записи в итоговый файл - заведи ещё 1 поток (таску).
3. Коммуникацию между N читающими и 1 пишущим организуй через System.Threading.Channel

Таким образом ты избавишься от ненужных блокировок и затрат на синхронизацию доступа к какому-то списку.
Похожие вопросы