RAD Server偶发AV排查:多线程写入字符串数组需加临界区吗?
多线程写入固定长度字符串数组是否需要临界区保护?
我在维护RAD Server时遇到偶发AV错误,怀疑问题可能出在无保护的多线程写入字符串数组操作中。示例代码如下:
function GetQuerys(SQLs: TStringDynArray): TStringDynArray; begin var JSONs: TStringDynArray; SetLength(JSONs, Length(SQLs)); TParallel.For(0, Length(SQLs) - 1, procedure(i: integer) begin var Query := TFDQuery.Create(nil); try var Cn := TFDConnection.Create(Query); // 每个Query使用独立的Connection // Connection会随Query释放,通过所有权关系自动清理 Cn.ConnectionDefName := 'MyPooledConnection'; // 连接池设置为Pooled=True以避免连接延迟 Query.Connection := Cn; Query.SQL.Text := SQLs[i]; Query.Open; var Stream := TStringStream.Create; try Query.SaveToStream(Stream, sfJSON); JSONs[i] := Stream.DataString; // 返回每个Query的JSON数据 finally Stream.Free; end; finally Query.Free; end; end ); Result := JSONs; end;
我知道多线程修改同一对象需用临界区保护,但写入上述JSONs数组是否也需要临界区?我认为不改变数组长度时,仅修改元素是安全的,且字符串数组本质是指向内存池字符串的指针数组。那么是否真的需要将JSONs[i] := Stream.DataString;替换为:
WritingLock.Acquire; try JSONs[i] := Stream.DataString; finally WritingLock.Release; end;
这会不会属于过度编程?
回答
不需要额外添加临界区,你的判断是正确的,这属于过度编程
原因如下:
- Delphi的
TStringDynArray是动态数组,在调用SetLength(JSONs, Length(SQLs))后,数组的内存布局完全固定,每个元素对应独立的内存地址(存储字符串指针)。 - 每个线程在
TParallel.For中操作的是唯一的索引i,不同线程的写入目标是完全独立的内存位置,不存在竞争条件。 - Delphi中字符串的赋值操作本质是原子的指针赋值(32位系统是4字节、64位系统是8字节,现代CPU对对齐的字长数据赋值是原子操作),不会出现指针被半写入的异常情况。
至于你遇到的偶发AV,建议排查其他方向:
- 检查
TFDConnection/TFDQuery的线程安全性,确认连接池配置是否存在多线程冲突 - 排查
Query.Open或SaveToStream过程中是否访问了未加保护的全局变量或共享资源 - 确认连接池的
Pooled=True配置是否正确,是否存在连接泄漏或重复使用的问题
内容的提问来源于stack exchange,提问作者Marc Guillot
相关产品推荐
相关产品推荐

