Я написал простой стековый узел (Delp [hi XE4, Win7-64, 32-битное приложение), где я могу иметь несколько «стеков» и поп/push узлов между ними из разных потоков одновременно. Он работает 99,999% времени, но в итоге сбой при стресс-тестировании с использованием всех ядер процессора.Delphi [volatile] и InterlockedCompareExchange не надежны?
урезанная, это сводится к этому (не реальный/скомпилированный код): Узлы:
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 push/pops каждый, и он обычно сбрасывается (безопасно из-за исключений с проверкой/поднятием) после нескольких циклов этих тестов, иногда многие многие циклы тестов завершаются до внезапного сбоя.
Любые советы будут оценены.
С уважением Антон E
InterlockedCompareExchange работает отлично, если вы передадите его правильно выровненным адресом. Для рабочего стека блокировки, очереди и динамической очереди используйте модуль OtlContainers из проекта www.omnithreadlibrary.com. – gabr
Функции 'InterlockedXXX', как известно, работают. Проблема в том, что ваш код этого не делает. –
Где находится документация для этого атрибута '[volatile]'? –