背景

公司做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

(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改写了这一段:

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进行访问,

而这些线程的行为不受控,在我方代码执行完之后,库内代码并未执行完,

而我方代码在下一循环中又在库内代码开启了新的线程,要对该文件进行使用,

在这个过程中,由于在循环体内使用了同一个变量来接收新值,自然就造成了我方代码执行完后该变量被作为垃圾回收,库内线程存取出现文件冲突的问题(无法写入文件),

从而报了这个错。

为了验证这个想法并使代码能跑起来,我把代码段复制了六个,接收的变量也改用数组,发现能跑了。

于是最终把阶段结果改用数组存储,成功解决问题:

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 =>
{
//实际处理消息
});
}

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

  1. loadrunner脚本中写入脚本输出log到外部文件,分析参数取值方式

    loadrunner脚本中写入脚本输出log到外部文件,分析参数取值方式 分类: 心得 loadrunner 我的测试 2012-04-01 12:52 2340人阅读 评论(0) 收藏 举报 脚本l ...

  2. 安装PHP过程中,make步骤报错:(集合网络上各种解决方法)

    安装PHP过程中,make步骤报错:(集合网络上各种解决方法) (1)-liconv -o sapi/fpm/php-fpm /usr/bin/ld: cannot find -liconv coll ...

  3. Python中,os.listdir遍历纯数字文件乱序如何解决

    Python中,os.listdir遍历纯数字文件乱序如何解决 日常跑深度学习视觉相关代码时,常常需要对数据集进行处理.许多图像文件名是利用纯数字递增的方式命名.通常所用的排序函数sort(),是按照 ...

  4. 在.NET Core中遭遇循环依赖问题"A circular dependency was detected"

    今天在将一个项目迁移至ASP.NET Core的过程中遭遇一个循环依赖问题,错误信息如下: A circular dependency was detected for the service of ...

  5. Windows系统中,循环运行.bat/.exe等文件

    一.创建循环运行的run-everySecond.vbs文件[双击次文件即可启动运行] dim a set a=CreateObject("Wscript.Shell") Do # ...

  6. js replace 全局替换 以表单的方式提交参数 判断是否为ie浏览器 将jquery.qqFace.js表情转换成微信的字符码 手机端省市区联动 新字体引用本地运行可以获得,放到服务器上报404 C#提取html中的汉字 MVC几种找不到资源的解决方式 使用Windows服务定时去执行一个方法的三种方式

    js replace 全局替换   js 的replace 默认替换只替换第一个匹配的字符,如果字符串有超过两个以上的对应字符就无法进行替换,这时候就要进行一点操作,进行全部替换. <scrip ...

  7. 64位win7中使用vs2013为python3.4安装pycrypto-2.6.1插件报Unable to find vcvarsall.bat异常解决方式

    问题描写叙述: 64位win7中使用vs2013为python3.4.2安装pycrypto-2.6.1插件报Unable to find vcvarsall.bat. 问题分析: 1.源代码分析,查 ...

  8. Java中使用POI读取大的Excel文件或者输入流时发生out of memory异常参考解决方案

    注意:此参考解决方案只是针对xlsx格式的excel文件! 背景 前一段时间遇到一种情况,服务器经常宕机,而且没有规律性,查看GC日志发生了out of memory,是堆溢出导致的,分析了一下堆的d ...

  9. python3中使用HTMLTestRunner.py报ImportError: No module named 'StringIO'的解决办法

    .原因是官网的是python2语法写的,看官手动把官网的HTMLTestRunner.py改成python3的语法: 参考:http://bbs.chinaunix.net/thread-415474 ...

随机推荐

  1. Scanner输入方法

    输入语句: * import java.util.Scanner; * System.out.println("请输入你想输入的东西:"); * Scanner (自定义)sc = ...

  2. PHP基础之面向对象篇

    前言 前面写的都是运算符.流程控制.排序查找等,下面说一说面向对象的一些内容.这是前面写的,有兴趣可以去看一看. PHP入门之类型与运算符 PHP入门之流程控制 PHP入门之函数 PHP入门之数组 P ...

  3. defer 延迟调用

    1. 延迟调用 defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个  xxx 函数的调用延迟到当前函数执行完后再执行. defer xxx()   这是一个很简单的例子,可以很快帮助 ...

  4. MongoDB入门(介绍、安装、增删改查)

    文章作者公众号bigsai,已收录在回车课堂,如有帮助还请不吝啬点个赞赞支持一下! 课程导学 大家好我是bigsai,我们都学过数据库,但你可能更熟悉关系(型)数据库例如MySQL,SQL SERVE ...

  5. 实现图片的上传(要求:上传到指定的FTP服务器)

    考核的知识点: (1)Linux系统的使用 (2)tengine 纯HTTP的web服务器 (3)SpringMVC的上传功能 (4)FTP的数据传到 1.1        传统上传方式的问题 但是在 ...

  6. axios之post提交

    axios官网地址:https://github.com/axios/axios post提交到后台需要做相对应的处理 使用URLSearchParams可以让post 数据提交到后台 对应gitHu ...

  7. Python练习题 010:分解质因数

    [Python练习题 010]将一个正整数分解质因数.例如:输入90,打印出90=2*3*3*5. -------------------------------------------------- ...

  8. 【小白学PyTorch】18 TF2构建自定义模型

    [机器学习炼丹术]的炼丹总群已经快满了,要加入的快联系炼丹兄WX:cyx645016617 参考目录: 目录 1 创建自定义网络层 2 创建一个完整的CNN 2.1 keras.Model vs ke ...

  9. P4279 [SHOI2008]小约翰的游戏(Anti_nim)

    Link 题面 题目描述 小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有 \(n\) 堆石子,小约翰和他的哥哥轮流取石子,每个人取的时候,可以随意选择一堆石子, 在这堆石子中取走任意多的石子,但不 ...

  10. dockerfile解析过程

    什么是dockerfile? DockerFile是用来构建docker镜像的文件,是由一系列命令和参数组成. 构建步骤? 1.编写dockerfile文件 2.docker build 3.dock ...