Я собрал вспомогательный класс для такого рода вещей. Это будет работать, если у вас есть контроль над всем, что будет иметь доступ к файлу. Если вы ожидаете раздоров из-за множества других вещей, то это ничего не стоит.
using System;
using System.IO;
using System.Threading;
/// <summary>
/// This is a wrapper aroung a FileStream. While it is not a Stream itself, it can be cast to
/// one (keep in mind that this might throw an exception).
/// </summary>
public class SafeFileStream: IDisposable
{
#region Private Members
private Mutex m_mutex;
private Stream m_stream;
private string m_path;
private FileMode m_fileMode;
private FileAccess m_fileAccess;
private FileShare m_fileShare;
#endregion//Private Members
#region Constructors
public SafeFileStream(string path, FileMode mode, FileAccess access, FileShare share)
{
m_mutex = new Mutex(false, String.Format("Global\\{0}", path.Replace('\\', '/')));
m_path = path;
m_fileMode = mode;
m_fileAccess = access;
m_fileShare = share;
}
#endregion//Constructors
#region Properties
public Stream UnderlyingStream
{
get
{
if (!IsOpen)
throw new InvalidOperationException("The underlying stream does not exist - try opening this stream.");
return m_stream;
}
}
public bool IsOpen
{
get { return m_stream != null; }
}
#endregion//Properties
#region Functions
/// <summary>
/// Opens the stream when it is not locked. If the file is locked, then
/// </summary>
public void Open()
{
if (m_stream != null)
throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
m_mutex.WaitOne();
m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
}
public bool TryOpen(TimeSpan span)
{
if (m_stream != null)
throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
if (m_mutex.WaitOne(span))
{
m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
return true;
}
else
return false;
}
public void Close()
{
if (m_stream != null)
{
m_stream.Close();
m_stream = null;
m_mutex.ReleaseMutex();
}
}
public void Dispose()
{
Close();
GC.SuppressFinalize(this);
}
public static explicit operator Stream(SafeFileStream sfs)
{
return sfs.UnderlyingStream;
}
#endregion//Functions
}
Работает с использованием именованного мьютекса. Те, кто хотят получить доступ к файлу, пытаются получить контроль над именованным мьютексом, который разделяет имя файла (с «\», превращенным в «/»). Вы можете либо использовать Open (), которая остановится до тех пор, пока мьютекс не станет доступен, либо вы можете использовать TryOpen (TimeSpan), который пытается получить мьютекс за заданную продолжительность и возвращает false, если он не может получить в течение промежутка времени. Скорее всего, это следует использовать внутри используемого блока, чтобы обеспечить правильное снятие блокировок, а поток (если он открыт) будет правильно удален при удалении этого объекта.
Я провел быструю проверку с ~ 20 вещами, чтобы сделать различные операции чтения / записи файла, и не увидел повреждений. Очевидно, что он не очень продвинут, но он должен работать для большинства простых случаев.