Запись в файл из нескольких потоков асинхронно с#
вот моя ситуация. Я хотел бы сделать запись в файловую систему максимально эффективной в моем приложении. Приложение многопоточное, и каждый поток может записывать в один и тот же файл. Есть ли способ, которым я могу писать в файл асинхронно из каждого потока, не имея записи в разных потоках, так сказать, головы вместе?
Я использую C# и .NET 3.5, и у меня также установлены реактивные расширения.
6 ответов
посмотреть Асинхронный Ввод/Вывод. Это освободит cpu, чтобы продолжить работу с другими задачами.
объединить с ReaderWriterLock как упоминал @Jack B Nimble
Если
запись в файловую систему, как эффективный, насколько это возможно
вы имеете в виду, что фактический ввод-вывод файла как можно быстрее, вам будет сложно ускорить его, диск просто физически медленнее. Может быть, SSD?
то, что я бы сделал, это отдельный рабочий поток(ы), посвященный задаче написания файлов. Когда один из ваших других потоков должен записать некоторые данные, он должен вызвать функцию для добавления данных в ArrayList (или какой-либо другой контейнер/класс). Внутри этой функции должен быть оператор lock вверху, чтобы предотвратить одновременное выполнение нескольких потоков. После добавления ссылки на ArrayList он возвращается и продолжает выполнять свои обязанности. Есть пара способов обработки потока(ов) записи. Вероятно, проще всего просто поместить его в бесконечный цикл с оператором sleep в конце, чтобы он не жевал ваш процессор(ы). Другой способ-использовать примитивы потоков и перейти в состояние ожидания, когда больше нет данных для записи. Этот метод подразумевает, что вам придется активировать поток с помощью чего-то вроде ManualResetEvent.метод SET.
существует множество различных способов чтения и записи файлов .Сеть. Я написал тестовую программу и даю результаты в своем блоге:
http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp
Я бы рекомендовал использовать методы Windows ReadFile и WriteFile, если вам нужна производительность. Избегайте любых асинхронных методов, так как мои результаты тестов показывают, что вы получаете лучшую производительность с синхронными методами ввода-вывода.
Боб Брайан MCSD
для тех, кто предпочитает код, я использую следующее для удаленного ведения журнала из веб-приложений...
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();
}
}
}
надеюсь, это сэкономит вам время
хотя блокировки на основе потоков могут решить эту проблему, существует способ, который работает между потоками, но, вероятно, лучше всего использовать, когда у вас есть несколько процессов записи до конца одним файлом.
чтобы получить это поведение между процессами (или потоками тоже), укажите, что вы хотите, чтобы atomic append записывал в операционную систему при создании дескрипторов файлов ОС. Это делается путем указания O_APPEND под Posix (Linux, Unix) и FILE_APPEND_DATA под Окна.
В C# вы не вызываете системные вызовы OS "open" или "CreateFile" напрямую, но есть способы получить этот результат.
Я спросил, как это сделать под Windows некоторое время назад, и получил два хороших ответов здесь: как я могу сделать атомарную запись / добавление в C# или как получить файлы, открытые с флагом FILE_APPEND_DATA?
в принципе, вы можете использовать FileStream () или PInvoke, я бы предложил FileStream () над PInvoke для очевидного причины.
вы можете использовать аргументы конструктора для FileStream (), чтобы указать асинхронный ввод-вывод файла в дополнение к FileSystemRights.Appenddata флаг, который должен дать вам как асинхронный ввод-вывод, так и атомарный добавление записи в файл.
предупреждение: некоторые ОС имеют ограничения на максимальное количество байтов, которые могут быть атомарно записаны таким образом, и превышение этого порога удалит обещание атомарности ОС.
из-за этого последнего gotcha, я бы рекомендовал остаться с lock() style contention management при попытке решить вашу проблему в рамках одного процесса.
использовать Читатель / Писатель блокировки для доступа к файловому потоку.
это форма, которая запускает число потоков, определенных в том же файле txt. Он был создан в .net 3.5 и winforms, я думаю, что вы можете адаптироваться к вашему использованию. Создайте форму и добавьте кнопку! Надеюсь, это поможет!
public partial class Form1 : Form
{
private const int NumberThreads = 39; //Total Threads
private const int NumberLines = 999; // Lines per Thread
private string _filePath = Path.Combine(Directory.GetCurrentDirectory(), "debug.txt");
private static readonly object _objectLock = new object();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (File.Exists(_filePath))
File.Delete(_filePath); //delete file
if (!File.Exists(_filePath))
using (var sw = File.CreateText(_filePath)) //create file
sw.WriteLine("TEST MULTITHREAD WRITTER"); //write header
//create threads
for (var i = 0; i <= NumberThreads; i++)
{
var thread = new Thread(ThreadWrite) { IsBackground = true };
thread.Start(); //start threads
}
}
private void ThreadWrite()
{
for (var i = 0; i <= NumberLines; i++)
{
//append a date in line
AppendLine(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"));
//sleeps a rand interval between 1 and 10 secs
Thread.Sleep(new Random().Next(1, 10));
}
}
private void AppendLine(string text)
{
lock (_objectLock)
{
var line = text;
using (var sw = new StreamWriter(_filePath, true))
{
//write line to file
sw.WriteLine(line);
//close file
sw.Close();
}
}
}
}