1. 程式人生 > >使用TLSharp進行Telegram中遭遇迴圈體內報session.dat檔案被佔用時解決方式一例

使用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 ```C# (await Listener.Client.SendRequestAsync(new TLRequestGetAllChats() { ExceptIds = new TeleSharp.TL.TLVector() }) .ConfigureAwait(false)) .Chats .Where(item => item.GetType() == typeof(TLChannel)) .Cast() .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() .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改寫了這一段: ```C# List AllGroups = (await Listener.Client.SendRequestAsync(new TLRequestGetAllChats() { ExceptIds = new TeleSharp.TL.TLVector() }) .ConfigureAwait(false)) .Chats .Where(item => item.GetType() == typeof(TLChannel)) .Cast() .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() .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進行訪問, 而這些執行緒的行為不受控,在我方程式碼執行完之後,庫內程式碼並未執行完, 而我方程式碼在下一迴圈中又在庫內程式碼開啟了新的執行緒,要對該檔案進行使用, 在這個過程中,由於在迴圈體內使用了同一個變數來接收新值,自然就造成了我方程式碼執行完後該變數被作為垃圾回收,庫內執行緒存取出現檔案衝突的問題(無法寫入檔案), 從而報了這個錯。 為了驗證這個想法並使程式碼能跑起來,我把程式碼段複製了六個,接收的變數也改用陣列,發現能跑了。 於是最終把階段結果改用陣列儲存,成功解決問題: ```C# List AllGroups = (await Listener.Client.SendRequestAsync(new TLRequestGetAllChats() { ExceptIds = new TeleSharp.TL.TLVector() }).ConfigureAwait(false)).Chats.Where(item => item.GetType() == typeof(TLChannel)).Cast().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().Where(item => (item.Entities == null || (item.Entities != null && item.Entities.Count() < 5)) && !string.IsNullOrWhiteSpace(item.Message)).ToList().ForEach(item => { //實際處理訊息 });