为何这段C#代码调用SaveRecentLogins时总是抛出IOException?
问题原因分析
你的SaveRecentLogins方法存在明显的文件操作冲突:
- 先用
File.OpenWrite(_fullFilePath)打开了一个文件流,并用using块持有这个流 - 紧接着又调用
await File.WriteAllTextAsync(_fullFilePath, json),而File.WriteAllTextAsync内部会再次尝试打开同一个文件进行写入操作
这两个操作会同时争夺文件的独占访问权,直接导致IOException提示文件被占用。
GetRecentLogins方法的实现是正确的,using块会确保FileStream在异步操作完成后被正确释放,多次调用读取操作本身不会导致文件占用问题。
修复方案
修改SaveRecentLogins方法,二选一即可:
方案一:直接使用File.WriteAllTextAsync(推荐)
去掉多余的File.OpenWrite操作,因为File.WriteAllTextAsync已经封装了完整的打开、写入、关闭流程:
public async Task SaveRecentLogins(IEnumerable<RecentAccount> recentLogins) { string json = JsonSerializer.Serialize(recentLogins, recentLogins.GetType()); if(json.Length > (1024 * 1024)) // File content would be > 1 MB { throw new Exception("File would be too large."); } Debug.WriteLine("Calling " + nameof(SaveRecentLogins)); // 直接调用WriteAllTextAsync,无需手动打开流 await File.WriteAllTextAsync(_fullFilePath, json); }
方案二:使用FileStream手动写入
如果需要更精细的流控制,就用FileStream来完成写入,不要调用File.WriteAllTextAsync:
public async Task SaveRecentLogins(IEnumerable<RecentAccount> recentLogins) { string json = JsonSerializer.Serialize(recentLogins, recentLogins.GetType()); if(json.Length > (1024 * 1024)) // File content would be > 1 MB { throw new Exception("File would be too large."); } Debug.WriteLine("Calling " + nameof(SaveRecentLogins)); using (FileStream fileStream = File.OpenWrite(_fullFilePath)) { // 使用JsonSerializer直接序列化到流 await JsonSerializer.SerializeAsync(fileStream, recentLogins); // 确保流的内容写入磁盘 await fileStream.FlushAsync(); } }
额外注意事项
- 确保所有调用
GetRecentLogins和SaveRecentLogins的地方都使用await,避免异步操作未完成就执行后续代码,导致文件被意外占用 - 可以给文件操作加个异步锁,避免多线程同时读写:
private readonly SemaphoreSlim _fileSemaphore = new SemaphoreSlim(1, 1); // 在Get方法中使用 public async Task<IEnumerable<RecentAccount>> GetRecentLogins() { await _fileSemaphore.WaitAsync(); try { // 原有的读取逻辑 } finally { _fileSemaphore.Release(); } }
内容的提问来源于stack exchange,提问作者user2190492
相关产品推荐
相关产品推荐

