高负载的.ashx http处理程序中,将记录追加到磁盘文件的最快且最安全的方法是什么?

3

在 .net4 IIS7 中,高度并行的 Web 环境下,写入(追加)记录到文件的最佳选项是什么?我使用 ashx http 处理程序接收应快速写入文件的小数据部分。一开始我使用了:

    using (var stream = new FileStream(fileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 8192))
    {
        stream.Write(buffer, 0, buffer.Length);
    } 

但我注意到一些记录被破坏或不完整,可能是由于FileShare.ReadWrite引起的。接下来我尝试将其更改为FileShare.Read。然后就没有破损的记录了,但是偶尔会出现这个异常:System.IO.IOException: The process cannot access the file ... because it is being used by another process.

理想情况下,我希望操作系统可以排队并发写请求,以便最终写入所有记录。我应该使用哪种文件访问API?

2个回答

3

根据文件的大小,有两种选择。如果文件较小,则最佳选项可能是通过某个共享锁来同步访问该文件。如果可能的话,最好保持文件打开(偶尔刷新),而不是经常打开/关闭。例如:

class MeaningfulName : IDisposable {
    FileStream file;
    readonly object syncLock = new object();
    public MeaningfulName(string path) {
        file =  new FileStream(fileName, FileMode.Append, FileAccess.Write,
           FileShare.ReadWrite, 8192);
    }
    public void Dispose() {
        if(file != null) {
           file.Dispose();
           file = null;
        }
    }
    public void Append(byte[] buffer) {
        if(file == null) throw new ObjectDisposedException(GetType().Name);
        lock(syncLock) { // only 1 thread can be appending at a time
            file.Write(buffer, 0, buffer.Length);
            file.Flush();
        }
    }
}

这是线程安全的,并且可以在没有问题的情况下提供给所有的ashx。

然而,对于更大量的数据,您可能需要查看一个同步读写队列 - 也就是说,所有的作者(ashx hits)都可以将数据抛到队列中,由单个专用的作者线程出列并追加。这将从ashx中删除IO时间,但是您可能需要限制队列大小,以防作者跟不上。这里有一个带容量限制的同步读写队列示例


好的,添加锁并保持文件打开似乎可以解决问题而不降低性能,但现在又出现了另一个问题。有多个文件需要写入,并且每小时会切换(创建新文件),因此我需要线程安全的集合来存储已打开的文件流,并在需要时添加新文件流。目前我使用ConcurrentDictionary<string,'MeaningfulName'>。想知道是否有更快的方法? - PanJanek
@PanJanek 你有没有写过与当前小时以外的任何东西?我不确定为什么你需要一个字典... - Marc Gravell
除了每小时更改文件外,我还将来自不同业务客户(由请求参数之一标识)的请求写入不同的文件。每小时大约会打开2-3个不同的文件。 - PanJanek

0

除非您正在使用Web Garden或Web Farm,否则我建议使用进程本地锁定(lock(){}),并尽可能在锁定之外执行尽可能多的处理。

如果您有多个要写入的文件,请参见更好的多线程谜题解决方案?以获取一个好的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接