C#操作chatslist.json报进程占用错误,未关闭流位置排查
问题根因分析
1. 核心错误:async void 导致异步操作未被等待
- 两个方法
SerializeChatsList、AddChatInList都定义为async void,这类方法属于“ fire-and-forget ”类型,调用方无法等待其执行完成:- 触发逻辑中调用
AddChatInList时不会等待它内部的读、写文件操作执行完成,会直接返回 - 上一次的文件读写流还没被
using块释放,下一次消息回调触发的文件操作就已经开始,直接导致文件占用冲突
- 触发逻辑中调用
2. 多线程并发无同步控制
Telegram Bot的消息回调是多线程并行触发的,多个消息同时到达时会并行发起文件读写操作,你没有加任何锁或者同步机制,原生的文件IO不支持多线程同时读写,必然会出现占用冲突。
3. 其他隐含BUG
SerializeChatsList中使用FileMode.Open打开文件写入,不会清空原有文件内容:如果新序列化的内容长度比旧内容短,文件末尾会残留旧内容,后续反序列化直接报错- 你粘贴的
ReadChatsList方法缺失右大括号,属于语法错误(如果实际运行代码确实存在会导致编译失败,大概率是粘贴遗漏)
修复方案
- 把所有
async void方法改成返回Task,调用时加await:
// 修改SerializeChatsList返回值 private async Task SerializeChatsList(ChatsInfo data) { // 把FileMode.Open改成FileMode.Create,写入前清空原有内容 using (FileStream fs = new FileStream(chatsListPath, FileMode.Create)) { await JsonSerializer.SerializeAsync<ChatsInfo>(fs, data, new JsonSerializerOptions { WriteIndented = true }); // 多余的fs.Close()可以去掉,using块会自动释放流 } } // 修改AddChatInList返回值 private async Task AddChatInList(long chat_id) { ChatsInfo chatsList = await ReadChatsList(); chatsList.activedChats.Add(chat_id); // 加await等待写入完成 await SerializeChatsList(chatsList); }
- 触发逻辑处加await等待AddChatInList执行完成:
if(!await ChatInTheList(message.Chat.Id)) { await AddChatInList(message.Chat.Id); }
- 增加读写锁控制并发,避免多线程同时操作文件:
可以在类里定义一个SemaphoreSlim对象作为锁:
// 类级别变量,初始化允许1个线程进入 private readonly SemaphoreSlim _fileLock = new SemaphoreSlim(1, 1);
然后在所有操作文件的方法(ReadChatsList、SerializeChatsList)入口处加锁,出口释放:
private async Task<ChatsInfo> ReadChatsList() { await _fileLock.WaitAsync(); try { // 原有ReadChatsList逻辑 } finally { _fileLock.Release(); } } private async Task SerializeChatsList(ChatsInfo data) { await _fileLock.WaitAsync(); try { // 原有SerializeChatsList逻辑 } finally { _fileLock.Release(); } }
内容的提问来源于stack exchange,提问作者pijio
相关产品推荐
相关产品推荐

