使用TLSharp进行Telegram中遭遇循环体内报session.dat文件被占用时解决方式一例

背景#

公司做Telegram开发,.net Framework项目,调用TLSharp作为框架进行开发。
开发需求是读取群里新到达的信息并进行过滤。
由此不可避免得要用到

TLSharp.Core.TelegramClient.GetHistoryAsync(TLAbsInputPeer peer, int offsetId = 0, int offsetDate = 0, int addOffset = 0, int limit = 100, int maxId = 0, int minId = 0, CancellationToken token = default);

这一方法。


由于每次都只能取得一个群的聊天历史记录,显然地在读取群列表之后第一想到地就是用linq

Copy
(await Listener.Client.SendRequestAsync<TLChats>(new TLRequestGetAllChats() { ExceptIds = new TeleSharp.TL.TLVector<int>() }) .ConfigureAwait(false)) .Chats .Where(item => item.GetType() == typeof(TLChannel)) .Cast<TLChannel>() .ToList() .ForEach(async item => { ((TLChannelMessages)await Listener.Client.GetHistoryAsync( peer: new TLInputPeerChannel() { ChannelId = item.Id, AccessHash = item.AccessHash.Value })) .Messages .Where(subitem => subitem.GetType() == typeof(TLMessage)) .Cast<TLMessage>() .Where(subitem => (subitem.Entities == null || (subitem.Entities != null && subitem.Entities.Count() < 5)) && !string.IsNullOrWhiteSpace(subitem.Message)) .ToList() .ForEach(subitem => { //实际处理消息 }); });

但是很意外的,跑挂了!

报出的原因是session.dat文件被占用。

探索#

session.dat文件是TG的消息会话文件,受TLSharp管控,因此不能自主去管理文件的打开关闭和释放。
于是抱着试一试的心理,把异步去掉了,再跑起来,还是一样的错误。
难道是Linq的问题?还是因为没有加ConfigAwait(false)?
这个框架下试了几次,均报session.dat被占用。

于是用foreach改写了这一段:

Copy
List<TLChannel> AllGroups = (await Listener.Client.SendRequestAsync<TLChats>(new TLRequestGetAllChats() { ExceptIds = new TeleSharp.TL.TLVector<int>() }) .ConfigureAwait(false)) .Chats .Where(item => item.GetType() == typeof(TLChannel)) .Cast<TLChannel>() .ToList(); foreach (TLChannel item in AllGroups) { ((TLChannelMessages)await Listener.Client.GetHistoryAsync( peer: new TLInputPeerChannel() { ChannelId = item.Id, AccessHash = item.AccessHash.Value })) .Messages .Where(subitem => subitem.GetType() == typeof(TLMessage)) .Cast<TLMessage>() .Where(subitem => (subitem.Entities == null || (subitem.Entities != null && subitem.Entities.Count() < 5)) && !string.IsNullOrWhiteSpace(subitem.Message)) .ToList() .ForEach(subitem => { //实际处理消息 }); };

继续跑,继续挂!!


然后其实又把foreach改成了for(;😉,问题依旧!!!

解决#

拆到for循环之后,因为方便断点了,发现每次出问题都不是在第一个数据,很大概率也不是发生在第二个数据,一般都是第三个才开始报占用错误,
这就带来了思考的空间。
很显然是循环体内的方法对session.dat的访问有要求,而循环上一条还没有结束,下一条就已经要访问。
为了验证这一点,手工用断点停几秒再执行,发现不报错了!
这就更能说明问题了:

TLSharp中的方法使用了多线程对session.dat进行访问,
而这些线程的行为不受控,在我方代码执行完之后,库内代码并未执行完,
而我方代码在下一循环中又在库内代码开启了新的线程,要对该文件进行使用,
在这个过程中,由于在循环体内使用了同一个变量来接收新值,自然就造成了我方代码执行完后该变量被作为垃圾回收,库内线程存取出现文件冲突的问题(无法写入文件),
从而报了这个错。

为了验证这个想法并使代码能跑起来,我把代码段复制了六个,接收的变量也改用数组,发现能跑了。
于是最终把阶段结果改用数组存储,成功解决问题:

Copy
List<TLChannel> AllGroups = (await Listener.Client.SendRequestAsync<TLChats>(new TLRequestGetAllChats() { ExceptIds = new TeleSharp.TL.TLVector<int>() }).ConfigureAwait(false)).Chats.Where(item => item.GetType() == typeof(TLChannel)).Cast<TLChannel>().ToList(); TLChannelMessages[] MessagesArray = new TLChannelMessages[AllGroups.Count]; for (int i = 0; i < AllGroups.Count; i++) { MessagesArray[i] = (TLChannelMessages)await Listener.Client.GetHistoryAsync(peer: new TLInputPeerChannel() { ChannelId = AllGroups[i].Id, AccessHash = AllGroups[i].AccessHash.Value }); MessagesArray[i].Messages.Where(item => item.GetType() == typeof(TLMessage)).Cast<TLMessage>().Where(item => (item.Entities == null || (item.Entities != null && item.Entities.Count() < 5)) && !string.IsNullOrWhiteSpace(item.Message)).ToList().ForEach(item => { //实际处理消息 }); }
posted @   布洛陀  阅读(541)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示
CONTENTS