我正在设计和开发一个api,其中多个线程从网上下载文件,然后将其写入磁盘。
如果使用不正确,可能会发生同一个文件被多个线程下载和写入,这会在写入磁盘时导致异常。
我想在编写文件的部分周围lock() { ... }
避免这个问题,但显然我不想用全局对象锁定,只是与该特定文件相关的东西,所以不是全部写入文件时会锁定线程。
我希望这个问题是可以理解的。
答案 0 :(得分:2)
您可以考虑使用ReaderWriterLockSlim
http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx
private ReaderWriterLockSlim fileLock = new ReaderWriterLockSlim();
fileLock.EnterWriteLock();
try
{
//write your file here
}
finally
{
fileLock.ExitWriteLock();
}
答案 1 :(得分:2)
所以你想要做的就是根据一个给定的密钥同步一堆动作。在这种情况下,该密钥可以是绝对文件名。我们可以将其实现为将键映射到某个同步对象的字典。如果我们想要实现阻塞同步机制,这可以是要锁定的对象,如果我们想要在适当时表示运行代码的异步方法,则可以是Task
;我和后来一起去了。我还使用ConcurrentDictionary
来让它处理同步,而不是手动处理它,并使用Lazy
来确保每个任务只创建一次:
public class KeyedSynchronizer<TKey>
{
private ConcurrentDictionary<TKey, Lazy<Task>> dictionary;
public KeyedSynchronizer(IEqualityComparer<TKey> comparer = null)
{
dictionary = new ConcurrentDictionary<TKey, Lazy<Task>>(
comparer ?? EqualityComparer<TKey>.Default);
}
public Task ActOnKey(TKey key, Action action)
{
var dictionaryValue = dictionary.AddOrUpdate(key,
new Lazy<Task>(() => Task.Run(action)),
(_, task) => new Lazy<Task>(() =>
task.Value.ContinueWith(t => action())));
return dictionaryValue.Value;
}
public static readonly KeyedSynchronizer<TKey> Default =
new KeyedSynchronizer<TKey>();
}
您现在可以创建此同步器的实例,然后指定操作以及它们对应的键(文件)。您可以确信,在完成对该文件的任何先前操作之前,不会执行这些操作。如果您想等到该操作完成,那么您可以Wait
执行任务,如果您没有任何需要等待,那么您可以不这样做。这也允许您通过等待任务异步进行处理。
答案 2 :(得分:0)
我遇到了类似情况,并通过lock()
对StreamWriter
对象解决了问题:
private Dictionary<string, StreamWriter> _writers; // Consider using a thread-safe dictionary
void WriteContent(string file, string content)
{
StreamWriter writer;
if (_writers.TryGetValue(file, out writer))
lock (writer)
writer.Write(content);
// Else handle missing writer
}
那是内存,它可能无法编译。我会读到安德鲁的解决方案(我将会),因为它可能更准确地说你需要...但如果你只是想要一个快速而肮脏的话,这非常简单。
答案 3 :(得分:0)
我会用一些解释作出答案。
Windows已经有了某种你想要的东西,背后的想法很简单:允许多个进程访问同一个文件并进行所有的写/读操作,这样:1)所有进程都在运行该文件的最新数据2)多次写入或读取而不等待(如果可能)。
它被称为Memory-Mapped Files。我主要是将它用于IPC(没有文件),所以无法提供示例,但应该有some。
您可以通过在其上使用一些缓冲区和排序层来模仿MMF行为,这会将所有读/写操作重定向到该缓冲区并定期将更新的内容刷新到物理文件中。
P.S:尝试查看文件共享(用于共享读/写的打开文件)。