Ответ 1
Как вы подходите к этому, это будет зависеть от того, как часто вы пишете. Если вы пишете относительно небольшое количество текста довольно редко, то просто используйте статический замок и делайте с ним. Это может быть вашим лучшим выбором в любом случае, потому что диск может удовлетворить только один запрос за раз. Предполагая, что все ваши выходные файлы находятся на одном диске (возможно, это не справедливое допущение, но несут меня), не будет большой разницы между блокировкой на уровне приложения и блокировкой, выполняемой на уровне ОС.
Итак, если вы объявите locker
как:
static object locker = new object();
Вы будете уверены, что в вашей программе нет конфликтов с другими потоками.
Если вы хотите, чтобы эта вещь была пуленепробиваемой (или, по крайней мере, разумно), вы не можете уйти от улавливания исключений. Плохие вещи могут случиться. Вы должны каким-то образом обрабатывать исключения. То, что вы делаете перед лицом ошибки, - это совсем другое. Вероятно, вы захотите повторить попытку несколько раз, если файл заблокирован. Если вы получаете неправильный путь или имя файла или полный диск или какой-либо из других ошибок, вы, вероятно, захотите убить программу. Опять же, это до вас. Но вы не можете избежать обработки исключений, если вы не справитесь с сбоем программы при ошибке.
Кстати, вы можете заменить весь этот код:
using (FileStream file = new FileStream(Filepath, FileMode.Append, FileAccess.Write, FileShare.Read))
using (StreamWriter writer = new StreamWriter(file, Encoding.Unicode))
{
writer.Write(text.ToString());
}
С помощью одного вызова:
File.AppendAllText(Filepath, text.ToString());
Предполагая, что вы используете .NET 4.0 или новее. См. File.AppendAllText.
Другим способом, с помощью которого вы могли бы справиться, является то, чтобы потоки записывали свои сообщения в очередь и имели выделенный поток, обслуживающий эту очередь. У вас будет BlockingCollection
сообщений и связанных путей к файлам. Например:
class LogMessage
{
public string Filepath { get; set; }
public string Text { get; set; }
}
BlockingCollection<LogMessage> _logMessages = new BlockingCollection<LogMessage>();
Ваши потоки записывают данные в эту очередь:
_logMessages.Add(new LogMessage("foo.log", "this is a test"));
Вы запускаете многолетнюю фоновую задачу, которая ничего не делает, кроме как обслуживать эту очередь:
foreach (var msg in _logMessages.GetConsumingEnumerable())
{
// of course you'll want your exception handling in here
File.AppendAllText(msg.Filepath, msg.Text);
}
Ваш потенциальный риск здесь заключается в том, что потоки создают сообщения слишком быстро, заставляя очередь расти без ограничений, потому что потребитель не может идти в ногу. Является ли это реальным риском в вашем приложении, вы только можете сказать. Если вы считаете, что это может быть рискован, вы можете поместить максимальный размер (количество записей) в очередь, чтобы, если размер очереди превышает это значение, производители будут ждать, пока в очереди не будет места, прежде чем они смогут добавить.