这是我的示例代码,使 FileUtil 类成为头部安全的文件 IO 处理程序。
public static class FileUtil {
private static ConcurrentDictionary<string, ReaderWriterLock> s_locks = new ConcurrentDictionary<string, ReaderWriterLock>();
public static string ReadFile(string path) {
var rwLock = s_locks.GetOrAdd(path, new ReaderWriterLock());
rwLock.AcquireReaderLock(1000);
string data = File.ReadAllText(path);
rwLock.ReleaseReaderLock();
return data;
}
public static void WriteFile(string path, string data) {
var rwLock = s_locks.GetOrAdd(path, new ReaderWriterLock());
rwLock.AcquireWriterLock(1000);
using (StreamWriter sw = new StreamWriter(path, true)) {
sw.Write(data);
}
rwLock.ReleaseWriterLock();
}
}
正如你所见,我创建了一个并发字典,为不同的文件持有不同的锁,以避免所有文件 IO 使用一个锁。我的实现正确吗?
最佳答案
不,有几个原因。
首先不要忘记文件系统为您提供了所需的并发性,然后您不需要专门实现任何内容。请注意,线程安全并不意味着资源不能同时访问,而是它的使用不会导致失败(在非常广泛的意义上)。
依赖操作系统实现并发的一种可能的实现是:
public static string ReadFile(string path) {
for (int retry=0; retry < 3; ++retry) {
try {
return File.ReadAllText(path);
}
catch (IOException e) {
// 0x80070020 is value for ERROR_SHARING_VIOLATION
if (Marshal.GetHRForException(e) == 0x80070020) {
Thread.Sleep(1000); // Wait and try again
continue;
}
throw;
}
}
}
代码 WriteFile()
很简单。
请注意,通过这种方式,您还将处理不同进程之间的共享违规,并且您将遵守打开文件时声明的共享规则;例如File.WriteAllText()
指定FileShare.Read
允许并发读取但不允许并发写入,请小心,因为读者可能会读取不是最新的内容,如果这不是您想要的,那么您应该删除 File.WriteAllText() 来指定 FileShare.None
在FileStream
构造函数。
关于一般用法的其他一些注释。
您正在使用ConcurrentDictionary<TKey, TValue>.GetOrAdd()
具有立即值。这意味着,即使字典已经包含 ReaderWriterLock
对于给定的路径,您每次都会构造一个新对象。在初始化期间不会获取锁,但它仍然是一个昂贵的操作,那么您应该(在其他代码中!)使用其他重载:
var rwLock = s_locks.GetOrAdd(path, () => new ReaderWriterLock());
这样ReadWriterLock
仅在需要时才会创建。
第二点是ReadWriterLock
本身,您可能想使用 ReadWriterLockSlim
相反,它是一个新的轻量级资源高效版本。你还应该考虑当资源获取超时、抛出异常时怎么办?重试吗?
最后一点是关于用法的:想想在您使用该代码 1000 个文件后,您的字典将包含 1000 个(可能未使用的)对象。在另一种情况下,您可以考虑在每次使用后处理它们。
关于c# - (C#) 使文件读/写线程安全(并考虑性能),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36301470/