我编写了一个简单的无锁节点堆栈(Delp [hi XE4,Win7-64,32位应用程序],我可以在其中同时从各个线程中拥有多个“堆栈”和pop / push节点。 它在99.999%的时间内工作,但最终在使用所有CPU内核的压力测试下出现故障。
剥离,它归结为此(不是真实/编译代码): 节点是:
type POBNode = ^TOBNode;
[volatile]TOBNode = record
[volatile]Next : POBNode;
Data : Int64;
end;
简化堆栈:
type TOBStack = class
private
[volatile]Head:POBNode;
function Pop:POBNode;
procedure Push(NewNode:POBNode);
end;
procedure TOBStack.Push(NewNode:POBNode);
var zTmp : POBNode;
begin;
repeat
zTmp:=InterlockedCompareExchangePointer(Pointer(Head),nil,nil);(memory fenced-read*)
NewNode.Next:=zTmp;
if InterlockedCompareExchangePointer(Head,NewNode,zTmp)=zTmp
then break (*success*)
else continue;
until false;
end;
function TOBStack.Pop:POBNode;
begin;
repeat
Result:=InterlockedCompareExchangePointer(Pointer(Head),nil,nil);(memory fenced-read*)
if Result=nil
the exit;
NewHead:=Result.Next;
if InterlockedCompareExchangePointer(Pointer(Head),NewHead,Result)=Result
then break (*Success*)
else continue;(*Fail, try again*)
until False;
end;
我已尝试过很多变种,但无法保持稳定。 如果我创建一些线程,每个线程都有一个堆栈,并且它们都是全局堆栈推送/弹出,它最终会出现故障,但不会很快。只有当我在多个线程中,在紧密的循环中强调它持续几分钟时。
我的代码中没有隐藏的错误,所以我需要更多的建议,而不是要求一个特定的问题让这个运行100%无错误,24/7。 对于无锁的线程安全堆栈,上面的代码是否正常? 我还能看到什么?这是不可能正常调试的,因为错误发生在各个地方,告诉我在某处发生指针或RAM损坏。我也得到重复的条目,这意味着一个节点弹出一个堆栈,然后又返回到同一个堆栈,仍然在旧堆栈的顶部...根据我的算法不可能发生?这让我相信它可能会违反Delphi / Windows InterlockedCompareExchange方法,或者我还有一些隐藏的知识尚未透露。 :)(我也尝试过TInterlocked)
我已经制作了一个完整的测试用例,可以从ftp.true.co.za复制。 在那里,我运行了8个线程,每个线程执行400,000个推送/弹出,并且在经过几个周期的测试后,它通常会崩溃(安全地由于检查/引发的异常),有时在一个突然崩溃之前完成许多测试周期。
任何建议都将受到赞赏。
此致 安东 ë
答案 0 :(得分:3)
起初我对这是一个ABA problem as indicated by gabr持怀疑态度。在我看来:如果一个线程查看当前Head
,另一个线程推送然后弹出;你很高兴仍以相同的方式在同一Head
上运作。
但是,请从Pop
方法中考虑这一点:
NewHead:=Result.Next;
if InterlockedCompareExchangePointer(Pointer(Head),NewHead,Result)=Result
NewHead
的值存储在本地变量中。Next
的值不同。NewHead
值。NewHead
的当前值不正确,从而破坏了您的堆栈。这个问题有一个微妙的变化甚至没有你的测试应用程序覆盖。在测试应用中未检查此问题,因为在测试结束之前,您不会销毁任何节点。
Head
。除以上形式外... ... 看看你的测试应用程序,还有一些非常狡猾的代码。 E.g。
您生成“随机数”:J:=GetTickCount and 7;(*Get a 'random' number 0..7*)
。
GetTickCount
会在紧密循环中产生大量重复项?您正在分配硬编码大小的内存:GetMem(zTmp,12);(*Allocate new node*)
。
SizeOf
?现在,鉴于这两个例子,我不完全相信你的测试代码中也没有错误。