gpt4 book ai didi

multithreading - 如何修复 TSparseArray

转载 作者:行者123 更新时间:2023-12-03 14:53:37 29 4
gpt4 key购买 nike

由于 System.Generics.Collections.TArray.Copy<T> 的未修复错误(取决于 already reported bug in System.CopyArray )使用线程库有时会引发异常。

方法 System.Threading.TSparseArray<T>.Add 中引发异常:

function TSparseArray<T>.Add(const Item: T): Integer;
var
I: Integer;
LArray, NewArray: TArray<T>;
begin
...
TArray.Copy<T>(LArray, NewArray, I + 1); // <- Exception here
...
end;

这是预期的错误 System.CopyArray 。因此,当尝试解决此问题时,我的第一个想法是简单地复制数组:

// TArray.Copy<T>(LArray, NewArray, I + 1); // <- Exception here
for LIdx := Low( LArray ) to High( LArray ) do
NewArray[LIdx] := LArray[LIdx];

就像魅力一样。但之后我想知道为什么需要数组副本:

LArray := FArray; // copy array reference from field
...
SetLength(NewArray, Length(LArray) * 2);
TArray.Copy<T>(LArray, NewArray, I + 1);
NewArray[I + 1] := Item;
Exit(I + 1);

元素被复制到 NewArray (局部变量)就是这样。没有分配回 FArray ,所以对我来说 NewArray超出范围时将最终确定。

现在我有三个错误修复选择:

  1. 只需替换 TArray.Copy

    SetLength(NewArray, Length(LArray) * 2);
    // TArray.Copy<T>(LArray, NewArray, I + 1); // <- Exception here
    for LIdx := Low( LArray ) to High( LArray ) do
    NewArray[LIdx] := LArray[LIdx];
    NewArray[I + 1] := Item;
    Exit(I + 1);
  2. 替换TArray.Copy并保存NewArray

    SetLength(NewArray, Length(LArray) * 2);
    // TArray.Copy<T>(LArray, NewArray, I + 1); // <- Exception here
    for LIdx := Low( LArray ) to High( LArray ) do
    NewArray[LIdx] := LArray[LIdx];
    NewArray[I + 1] := Item;
    FArray := NewArray;
    Exit(I + 1);
  3. 注释掉所有不必要的代码部分(因为它们只是浪费时间)

    // SetLength(NewArray, Length(LArray) * 2);
    // TArray.Copy<T>(LArray, NewArray, I + 1); // <- Exception here
    // NewArray[I + 1] := Item;
    Exit(I + 1);

我用一堆任务检查了所有三个修复,寻找未使用的工作线程或未执行的任务。但我没有找到其中任何一个。该库按预期工作(现在没有任何异常)。

你能指出我在这里缺少什么吗?

<小时/>

为了获得此异常,您运行了一堆任务并让 TTaskPool创造越来越多TWorkerQueueThreads 。通过 TaskManager 检查线程数并在 TArray.Copy 上使用断点线路 TSparseArray<T>.Add方法。当应用程序的线程数超过 25 个线程时,我会遇到此异常。

// Hit the button very fast until the debugger stops 
// at TSparseArray<T>.Add method to copy the array
procedure TForm1.Button1Click( Sender : TObject );
var
LIdx : Integer;
begin
for LIdx := 1 to 20 do
TTask.Run(
procedure
begin
Sleep( 50 );
end );
end;

最佳答案

这不是 System.CopyArray 中的错误。根据设计,它仅支持托管类型。该错误实际上位于 TArray.Copy<T> 。那是错误的调用System.CopyArray不区分是否 T是托管类型。

但是,最新版本TArray.Copy<T> ,从 XE7 update 1 开始,似乎没有遇到您所描述的问题。代码如下所示:

class procedure TArray.Copy<T>(const Source, Destination: array of T; 
SourceIndex, DestIndex, Count: NativeInt);
begin
CheckArrays(Pointer(@Source[0]), Pointer(@Destination[0]), SourceIndex,
Length(Source), DestIndex, Length(Destination), Count);
if IsManagedType(T) then
System.CopyArray(Pointer(@Destination[SourceIndex]),
Pointer(@Source[SourceIndex]), TypeInfo(T), Count)
else
System.Move(Pointer(@Destination[SourceIndex])^, Pointer(@Source[SourceIndex])^,
Count * SizeOf(T));
end;

除非我的分析有误,否则您只需应用更新 1 即可解决 System.CopyArray 的问题。 .

<小时/>

但正如 Uwe 在下面的评论中指出的那样,这段代码仍然是伪造的。它使用 SourceIndex错误地在DestIndex应该使用。并且源参数和目标参数以错误的顺序传递。人们还想知道作者为什么写Pointer(@Destination[SourceIndex])^而不是Destination[SourceIndex] 。我发现这整个情况非常令人沮丧。 Embarcadero 如何发布质量如此糟糕的代码?

<小时/>

比上面更深的是TSparseArray<T>的问题。看起来像这样:

function TSparseArray<T>.Add(const Item: T): Integer;
var
I: Integer;
LArray, NewArray: TArray<T>;
begin
while True do
begin
LArray := FArray;
TMonitor.Enter(FLock);
try
for I := 0 to Length(LArray) - 1 do
begin
if LArray[I] = nil then
begin
FArray[I] := Item;
Exit(I);
end else if I = Length(LArray) - 1 then
begin
if LArray <> FArray then
Continue;
SetLength(NewArray, Length(LArray) * 2);
TArray.Copy<T>(LArray, NewArray, I + 1);
NewArray[I + 1] := Item;
Exit(I + 1);
end;
end;
finally
TMonitor.Exit(FLock);
end;
end;
end;

唯一一次FArray初始化是在TSparseArray<T>中构造函数。这意味着如果数组已满,则会添加和丢失项目。大概是I = Length(LArray) - 1旨在延长 FArray 的长度并捕获新项目。但是,还要注意 TSparseArray<T>暴露FArray通过Current属性(property)。并且这种暴露不受锁的保护。所以,我看不到这个类一次 FArray 可以如何以任何有用的方式表现。变满。

我建议您构建一个示例,其中 FArray变满表明添加的项目丢失了。提交一份错误报告来证明这一点,并链接到这个问题。

关于multithreading - 如何修复 TSparseArray<T>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27754399/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com