将使用System.IO.StreamWriter&;WriteLineAsync等待另一个线程正在访问的文件

本文关键字:线程 另一个 等待 文件 访问 WriteLineAsync System IO StreamWriter amp | 更新日期: 2023-09-27 17:58:00

我的asp.net mvc-5 web应用程序中有以下代码:-

System.IO.StreamWriter file =
new System.IO.StreamWriter(Server.MapPath("~/logs/logs.txt"), true);
await file.WriteLineAsync("execution started at " + System.DateTime.Now.ToString());
file.Close();

现在我的问题是,如果两个方法同时执行上面的代码,会发生什么?其中一个会失败吗?因为它将试图写入另一个线程正在访问的txt文件?

将使用System.IO.StreamWriter&;WriteLineAsync等待另一个线程正在访问的文件

您可以很好地利用TPL数据流来实现这一点,这样您就可以获得一个队列和一种机制,通过该机制,对文件的写入永远不会并行发生(通过使用MaxDegreeOfParallelism

public class Logger
{
    private ActionBlock<string> block;
    public Logger(string filePath)
    {
        block = new ActionBlock<string>(async message => {
            using(var f = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
            {
                f.Position = f.Length;
                using(var sw = new StreamWriter(f))
                {
                    await sw.WriteLineAsync(message);
                }
            }
        }, new ExecutionDataflowBlockOptions{MaxDegreeOfParallelism = 1});
    }
    public void Log(string message)
    {
        block.Post(message);
    }
}

当您用字符串打开StreamWriter时,它会执行以下操作来创建流

private static Stream CreateFile(String path, bool append, bool checkHost) {
    FileMode mode = append? FileMode.Append: FileMode.Create;
    FileStream f = new FileStream(path, mode, FileAccess.Write, FileShare.Read,
        DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);
    return f;
}

因为它执行FileAccess.Write, FileShare.Read,这意味着它请求写入权限,并且只允许其他人在请求读取权限时打开文件。因为两个调用都将请求写入权限,所以第二个调用将失败,并出现"文件正在使用"错误。


解决这个问题的一种方法是使用SemaphoreSlim来阻止对文件的访问,这样一次只允许一个写入程序。

//Elsewhere
private static SemaphoreSlim _fileLock = new SemaphoreSlim(1);

//In your function.
try
{
    await _fileLock.WaitAsync();
    using(var file = new System.IO.StreamWriter(Server.MapPath("~/logs/logs.txt"), true))
    {
        await file.WriteLineAsync("execution started at " + System.DateTime.Now.ToString());
    }
}
finally
{
    _fileLock.Release();
}

然而,更好的解决方案是找到一个第三方日志库,它可以像Log4Net一样为您处理此问题,这样您就不需要担心锁定或并发问题。

是。您将得到一个错误,该文件正被另一个进程使用。最好的办法是用try catch包围该块并处理异常,然后在文件可用后重试。