Delphi2007 Indy9下IdThreadComponent调用TThread.Queue编译报错求助
问题根因
编译报错、数据丢失的核心原因非常明确:
- Delphi 2007 是早于匿名方法特性发布的版本,匿名过程语法从Delphi 2009才正式支持,编译器无法识别
TThread.Queue参数里内嵌的procedure结构,直接抛出语法错误。 - Delphi 2007 自带的
TThread.Queue仅支持传入无参数的对象方法(TThreadMethod类型),没有适配匿名过程的重载版本,即使跳过语法检查也无法正常运行。 - 直接去掉同步逻辑在工作线程操作VCL控件,本身就违反VCL单线程访问规则,会随机出现访问冲突、写入覆盖,自然会丢数据。不要图省事直接在线程里写
Memo1.Text := s,哪怕跑通几次也是隐患。
适配Delphi 2007 + Indy 9的实现方案
采用线程安全队列做中转,配合D2007原生支持的异步队列同步,既不阻塞读条码的工作线程,也不会丢数据:
- 在窗体层维护一个字符串队列+临界区对象,做工作线程和UI线程的数据中转
- 工作线程每次读到有效条码后,加锁将数据压入队列,再调用
TThread.Queue触发UI同步,全程不阻塞读操作 - UI线程执行同步方法时,一次性取出队列中所有缓存的条码逐个处理,避免单变量中转时新数据覆盖旧数据的问题
- 注意核对Indy9版本
ReadLn的参数顺序,Indy9和Indy10的部分重载参数顺序有差异,不要传反导致读超时、读截断
适配后的可直接运行的代码如下:
// 窗体类定义部分 type TPkgSendF1 = class(TForm) IdTCPClient1: TIdTCPClient; IdThreadComponent1: TIdThreadComponent; Memo1: TMemo; procedure IdThreadComponent1Run(Sender: TIdCustomThreadComponent); private FBarcodeQueue: TStringList; FQueueLock: TRTLCriticalSection; procedure SyncProcessBarcode; procedure ProcessRead(AValue: string); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; end; // 窗体实现部分 constructor TPkgSendF1.Create(AOwner: TComponent); begin inherited; InitializeCriticalSection(FQueueLock); FBarcodeQueue := TStringList.Create; end; destructor TPkgSendF1.Destroy; begin DeleteCriticalSection(FQueueLock); FBarcodeQueue.Free; inherited; end; procedure TPkgSendF1.IdThreadComponent1Run(Sender: TIdCustomThreadComponent); var s: string; begin // 注意核对Indy9下ReadLn的参数顺序,确保结束符、超时参数匹配版本定义 s := IdTCPClient1.ReadLn('&', 20000, 1500); if s = '' then Exit; // 超时无有效数据直接返回,不压入空内容 // 加锁将读到的条码压入安全队列 EnterCriticalSection(FQueueLock); try FBarcodeQueue.Add(s); finally LeaveCriticalSection(FQueueLock); end; // D2007下Queue仅支持无参数对象方法,直接传入同步方法即可,异步执行不阻塞工作线程 TThread.Queue(nil, SyncProcessBarcode); end; procedure TPkgSendF1.SyncProcessBarcode; var tmpList: TStringList; i: Integer; begin tmpList := TStringList.Create; try // 加锁一次性取出所有待处理数据,缩短锁持有时间,避免阻塞工作线程入队 EnterCriticalSection(FQueueLock); try tmpList.AddStrings(FBarcodeQueue); FBarcodeQueue.Clear; finally LeaveCriticalSection(FQueueLock); end; // UI线程中逐个处理缓存的条码,无覆盖丢失问题 for i := 0 to tmpList.Count - 1 do begin ProcessRead(tmpList[i]); end; finally tmpList.Free; end; end; procedure TPkgSendF1.ProcessRead(AValue: string); begin // 所有UI操作都在主线程执行,无线程安全问题 Memo1.Text := AValue; end;
补充说明:不要用
TThread.Synchronize替换TThread.Queue,Synchronize会阻塞工作线程直到UI方法执行完成,如果UI线程卡顿会直接导致条码读取超时丢数,Queue是异步排队执行,不会影响工作线程的读取效率。
内容的提问来源于stack exchange,提问作者FAlvarado
相关产品推荐
相关产品推荐

