Как мне сделать выполнение метода многопоточным(каждый вызов в отдельном потоке)?

Рейтинг: 0Ответов: 1Опубликовано: 03.08.2023

мне нужно обрабатывать файлы в директории, которую выберет пользователь. Притом каждый файл в отдельном потоке, чтобы не подвисал интерфейс. Каждый файлик читается и в нём считается кол-во каждого байта(то есть 256 значений). В процессе обработки результаты по каждому файлу должны выводиться на экран(я взял richtextbox ( RTB ) для этого) + время обработки. есть класс FilesHandler, в котором идёт обработка директории:

class FilesHandler
{
    private List<string> PathesOfFiles = new List<string>(); //Содержит пути к файлам

    public void LoadPathes(string pathOfDirectory) //Загружает адреса файлов
    {
        string[] allfiles = Directory.GetFiles(pathOfDirectory);
        foreach (string filename in allfiles)
        {
            PathesOfFiles.Add(filename);
        }
    }

    public void HandleEachFile(RichTextBox RTB) //обработчик каждого файла
    {
        foreach (string path in PathesOfFiles)
            SimpleMethod(RTB, path);
    }

    public void SimpleMethod(RichTextBox RTB, string path)
    {
        List<string> resultLog = new List<string>();
        Stopwatch stopwatch = new Stopwatch(); //Создаём секундомер
        stopwatch.Start(); //Запускаем секундомер
        resultLog.Add(GetFileName(path) + " {" + string.Join(", ", CountBytes(path)) + " } "); //путь + массив байтов
        stopwatch.Stop(); //Останавливаем секундомер, чтобы записать время обработки файла
        resultLog.Add(stopwatch.ElapsedMilliseconds.ToString() + " ms"); //+ время обработки
        RTB.Text += String.Join("", resultLog) + '\n'; //вывод лога файла в richtextbox
    }

    private string GetFileName(string path)
    {
        return System.IO.Path.GetFileName(path);
    }

    private ulong[] CountBytes(string filename)
    {
        ulong[] Bytes = new ulong[256];

        byte[] buffer = new byte[4096 * 1000];
        int bytesRead = 0;
        using (FileStream reader = File.OpenRead(filename))
            while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0)
                for (int i = 0; i < bytesRead; Bytes[buffer[i++]]++) ;

        return Bytes;
    }
}

Я ранее задавал вопрос про потоки, но не понял, как это сделать качественно и без костылей. Подскажите, пожалуйста, как сделать это многопоточным способом(каждый файл обрабатывается в отдельном потоке, такое задание). Я запутался в Threads, Parallel, Task и т.д. И главное, чтобы способ с потоками работал побыстрее чем линейный. Спасибо!!!

UPD: Забыл добавить, когда пытался с потоками работать, вылезало исключение, что компонент формы(richtextbox) вызывается не из того потока, где был создан. Подскажите, как это лучше исправить. я пробовал так, но, мне кажется, есть способ получше

        if (RTB.InvokeRequired)
        {
            RTB.BeginInvoke((MethodInvoker)(() =>
            {
                RTB.Text += String.Join("", resultLog) + '\n';
            }
            ));
        }
        else
        {
            RTB.Text += String.Join("", resultLog) + '\n';
        }

Ответы

▲ 1

Всё просто:

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

public void HandleEachFile(RichTextBox RTB) //обработчик каждого файла
{
    List<Task> tasks = new(PathesOfFiles.length);
    foreach (string path in PathesOfFiles)    
        tasks.AddLast(new Task(() => SimpleMethod(RTB, path)));

    // Ожидаем завершения всех задач
    // (Убрать по желанию)
    Task.WaitAll(tasks.ToArray()); 
}

Так же скорее всего необходимо добавить потокобезопасность для RTB в SimpleMethod:

// Блокируем RTB что бы не использовать его одновременно из разных потоков
Action appendText = () => RTB.Text += string.Join("", resultLog) + '\n';
if (RTB.InvokeRequired)
    RTB.BeginInvoke(appendText);
else
    appendText();

Таким образом RTB в конкретный момент времени будет изменяться только одним потоком