Это немного длинный вопрос, но здесь мы идем. Существует версия FormatDateTime, которая называется поточно-ориентированной, поскольку вы используете
GetLocaleFormatSettings(3081, FormatSettings);
чтобы получить значение, а затем вы можете использовать его вот так;
FormatDateTime('yyyy', 0, FormatSettings);
Теперь представьте два таймера, один из которых использует TTimer (скажем, интервал 1000 мс), а затем другой таймер, созданный так (интервал 10 мс);
CreateTimerQueueTimer
(
FQueueTimer,
0,
TimerCallback,
nil,
10,
10,
WT_EXECUTEINTIMERTHREAD
);
Теперь немного, если в обратном вызове, а также в событии таймера у вас есть следующий код:
for i := 1 to 10000 do
begin
FormatDateTime('yyyy', 0, FormatSettings);
end;
Обратите внимание, что назначения нет. Это приводит к нарушениям доступа почти сразу, иногда через 20 минут, что угодно, в произвольных местах. Теперь, если вы пишете этот код в C ++ Builder, он никогда не падает. Мы используем преобразование заголовков JEDI JwaXXXX. Даже если мы поместим блокировки в версию Delphi вокруг кода, это только задержит неизбежное. Мы рассмотрели исходные файлы заголовков C и все выглядит хорошо, есть ли другой способ использования C ++ среды выполнения Delphi? Потокобезопасная версия FormatDatTime выглядит повторно входящей. Любые идеи или мысли от любого, кто, возможно, видел это раньше.
UPDATE:
Чтобы немного сузить это, FormatSettings передается как const, так имеет ли значение, если они используют одну и ту же копию (как оказалось, передача локальной версии в вызове функции приводит к той же проблеме)? Также версия FormatDateTime, которая принимает FormatSettings, не вызывает GetThreadLocale, потому что она уже имеет информацию о Locale в структуре FormatSettings (я дважды проверил, шагая по коду).
Я упомянул об отсутствии назначения, чтобы было ясно, что к общему хранилищу не обращаются, поэтому блокировка не требуется.
WT_EXECUTEINTIMERTHREAD используется для упрощения проблемы. У меня сложилось впечатление, что вы должны использовать его только для очень коротких задач, потому что это может означать, что он пропустит следующий интервал, если он выполнит что-то длинное?
Если вы используете простой старый TThread, проблема не возникает. Я предполагаю, что использование TThread или TTimer работает, но использование потока, созданного вне VCL, не работает, поэтому я спросил, есть ли разница в том, как C ++ Builder использует VCL / Delphi RTL.
Кроме этого, упомянутый выше код также не работает (но занимает больше времени), через некоторое время CS: = TCriticalSection.Create;
CS.Acquire;
for i := 1 to LoopCount do
begin
FormatDateTime('yyyy', 0, FormatSettings);
end;
CS.Release;
А теперь я действительно не понимаю, я написал это как предложено;
function ReturnAString: string;
begin
Result := 'Test';
UniqueString(Result);
end;
, а затем внутри каждого типа таймера код:
for i := 1 to 10000 do
begin
ReturnAString;
end;
Это вызывает те же виды сбоев, как я уже говорил ранее, ошибка никогда не находится в одном и том же месте внутри окна ЦП и т. Д. Иногда это нарушение доступа, а иногда это может быть недопустимая операция с указателем. Я использую Delphi 2009 между прочим.
ОБНОВЛЕНИЕ 2:
Родди (ниже) указывает на событие Ontimer (и, к сожалению, также Winsock, то есть TClientSocket) использует насос сообщений Windows (кроме того, было бы неплохо иметь несколько хороших компонентов Winsock2, использующих IOCP и Overlapping IO), следовательно, push уйти от этого. Однако кто-нибудь знает, как узнать, какой тип локального хранилища потока настроен в CreateQueueTimerQueue?
Спасибо, что нашли время подумать и ответить на эту проблему.