Запись в файл из нескольких потоков асинхронно c # - PullRequest
36 голосов
/ 18 августа 2010

Вот моя ситуация. Я хотел бы сделать запись в файловую систему максимально эффективной в моем приложении. Приложение является многопоточным, и каждый поток может записывать в один и тот же файл. Есть ли способ, которым я могу записать в файл асинхронно из каждого потока, не записывая, так сказать, записи в разные потоки?

Я использую C # и .NET 3.5, и у меня также установлены Reactive Extensions.

Ответы [ 5 ]

14 голосов
/ 18 августа 2010

Посмотрите на Асинхронный ввод / вывод . Это освободит процессор для продолжения выполнения других задач.
Объединить с ReaderWriterLock как упомянуто @Jack B Шустрая

Если по

запись в файловую систему как максимально эффективный

Вы имеете в виду максимально быстрое выполнение реального файлового ввода-вывода. Вам будет очень тяжело ускорить его, а диск просто физически медленнее. Может SSD?

9 голосов
/ 21 декабря 2016

Для тех, кто предпочитает код, я использую следующее для удаленной регистрации из веб-приложений ...

public static class LoggingExtensions
{
    static ReaderWriterLock locker = new ReaderWriterLock();
    public static void WriteDebug(this string text)
    {
        try
        {
            locker.AcquireWriterLock(int.MaxValue); //You might wanna change timeout value 
            System.IO.File.AppendAllLines(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\", ""), "debug.txt"), new[] { text });
        }
        finally
        {
            locker.ReleaseWriterLock();
        }
    }
}

Надеюсь, это сэкономит вам время

7 голосов
/ 04 марта 2011

То, что я хотел бы сделать, - это иметь отдельный рабочий поток (-ы), посвященный задаче записи файлов. Когда один из ваших других потоков должен записать некоторые данные, он должен вызвать функцию для добавления данных в ArrayList (или некоторый другой контейнер / класс). Внутри этой функции должен быть оператор блокировки вверху, чтобы предотвратить одновременное выполнение нескольких потоков. После добавления ссылки на ArrayList он возвращается и продолжает работу по дому. Есть несколько способов обработки потока (ов) письма. Вероятно, самое простое - просто поместить его в бесконечный цикл с оператором sleep в конце, чтобы он не перегружал ваши процессоры. Другой способ - использовать потоковые примитивы и переходить в состояние ожидания, когда больше нет данных для записи. Этот метод подразумевает, что вам нужно будет активировать поток чем-то вроде метода ManualResetEvent.Set.

Существует много разных способов считывания и записи файлов в .NET. Я написал тестовую программу и опубликовал результаты в своем блоге:

http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp

Я бы порекомендовал использовать методы Windows ReadFile и WriteFile, если вам нужна производительность. Избегайте любых асинхронных методов, так как мои результаты тестов показывают, что вы получаете лучшую производительность с синхронными методами ввода / вывода.

5 голосов
/ 30 марта 2012

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

Чтобы получить такое поведение для процессов (или потоков тоже), укажите, что вы хотите, чтобы атомарные записи добавления в операционную систему создавались при создании дескрипторов файла ОС.Это можно сделать, указав O_APPEND в Posix (Linux, Unix) и FILE_APPEND_DATA в Windows.

В C # вы не называете ОС «open» или системными вызовами «CreateFile» напрямую, но есть способычтобы получить этот результат.

Я спросил, как это сделать под Windows, и получил два хороших ответа здесь: Как я могу сделать атомарную запись / добавление в C #, или как я могу получитьфайлы, открытые с флагом FILE_APPEND_DATA?

По сути, вы можете использовать FileStream () или PInvoke, я бы предложил FileStream () поверх PInvoke по очевидным причинам.

Вы можете использовать аргументы конструкторав FileStream (), чтобы указать асинхронный файловый ввод-вывод в дополнение к флагу FileSystemRights.AppendData, который должен предоставить вам как асинхронный ввод-вывод, так и атомарные записи в файл.

Предупреждение. Некоторые ОС имеют ограничения намаксимальное количество байтов, которое может быть атомарно записано таким образом, и превышение этого порога устранит обещание атомарности ОС.

Из-за этой последней ошибкиЯ бы порекомендовал придерживаться управления конфликтами в стиле lock () при попытке решить вашу проблему в рамках одного процесса.

4 голосов
/ 18 августа 2010

Используйте Reader / Writer блокировки для доступа к потоку файлов.

...