首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Delphi线程和线程安全FormatDateTime崩溃

Delphi线程和线程安全FormatDateTime崩溃
EN

Stack Overflow用户
提问于 2008-12-09 12:05:21
回答 8查看 4.3K关注 0票数 4

这是一个很长的问题,但我们开始了。有一个版本的FormatDateTime据说是线程安全的,因为您使用

代码语言:javascript
复制
GetLocaleFormatSettings(3081, FormatSettings); 

得到一个值,然后你可以这样使用它;

代码语言:javascript
复制
FormatDateTime('yyyy', 0, FormatSettings); 

现在设想两个定时器,一个使用TTimer (间隔为1000 so ),然后另一个定时器创建为这样(10 so间隔);

代码语言:javascript
复制
CreateTimerQueueTimer
(
  FQueueTimer, 
  0, 
  TimerCallback, 
  nil, 
  10, 
  10, 
  WT_EXECUTEINTIMERTHREAD
);

现在的位,如果在回调和计时器事件,你有以下的代码;

代码语言:javascript
复制
for i := 1 to 10000 do
begin
  FormatDateTime('yyyy', 0, FormatSettings);
end;

注:没有作业。这几乎立即导致访问违规,有时是20分钟后,随便什么地方。现在,如果用C++Builder编写这段代码,它就不会崩溃。我们正在使用的标题转换是绝地JwaXXXX的转换。即使我们围绕代码在Delphi版本中设置锁,它也只会延迟不可避免的发生。我们已经查看了原始的C头文件,它们看起来都很好,C++使用Delphi有什么不同的方式吗?线程安全版本的FormatDatTime看起来是重新进入。任何可能曾见过这种想法或想法的人。

更新:

为了缩小这一点,FormatSettings作为一个const传入,那么如果他们使用相同的副本(结果是在函数调用中传递一个本地版本会导致同样的问题),这有什么关系吗?另外,接受FormatDateTime的FormatSettings版本不调用GetThreadLocale,因为它已经在FormatSettings结构中包含了区域设置信息(我通过遍历代码进行了双重检查)。

我提到没有分配来明确表示没有共享存储被访问,所以不需要锁定。

WT_EXECUTEINTIMERTHREAD是用来简化问题的。我的印象是,你应该只在很短的任务中使用它,因为它可能意味着如果它运行很长时间,它就会错过下一个间隔?

如果您使用的是普通的旧TThread,那么问题就不会发生。我在这里要了解的是,使用TThread或TTimer是可行的,但是使用在VCL之外创建的线程不起作用,这就是为什么我问C++生成器使用VCL/Delphi的方式是否有区别。

首先,前面提到的这段代码也会失败(但花费的时间更长),一段时间后,CS := TCriticalSection.Create;

代码语言:javascript
复制
  CS.Acquire;
  for i := 1 to LoopCount do
  begin
    FormatDateTime('yyyy', 0, FormatSettings);
  end;
  CS.Release;

现在我真的不明白了,我按照建议写了这个;

代码语言:javascript
复制
function ReturnAString: string;
begin
  Result := 'Test';
  UniqueString(Result);
end;

然后在每种类型的定时器内,代码是;

代码语言:javascript
复制
  for i := 1 to 10000 do
  begin
    ReturnAString;
  end;

这会导致相同类型的故障,就像我之前说过的那样,故障从来不在CPU窗口内的同一个位置,有时是访问冲突,有时可能是无效的指针操作。我正在使用德尔福2009顺便。

更新2:

Roddy (下面)指出了Ontimer事件(不幸的是,Winsock,也就是TClientSocket)使用了windows消息泵(顺便说一下,如果使用IOCP和重叠IO拥有一些好的Winsock2组件将是很好的),因此就可以摆脱它了。但是,有谁知道如何在CreateQueueTimerQueue上设置什么样的线程本地存储呢?

谢谢你花时间思考和回答这个问题。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2008-12-10 01:34:34

我不确定用“答案”回答我自己的问题是否好,但这似乎合乎逻辑,请告诉我这是否不酷。

我想我已经发现了问题,线程本地存储的想法让我跟随了一堆线索,我发现了这条神奇的线条;

IsMultiThread := True;

从帮助;

"IsMultiThread设置为true,表示内存管理器应该支持多个线程。IsMultiThread被BeginThread和类工厂设置为true。“

当然,这是而不是通过使用TTimer的单个主VCL线程设置的,但是在使用TThread时是为您设置的。如果我手动设置它,问题就会消失。

在C++Builder中,我不使用TThread,但使用以下代码显示;

代码语言:javascript
复制
if (IsMultiThread) {
  ShowMessage("IsMultiThread is True!");
}

这是自动为你设定的。

我真的很高兴人们的意见,以便我可以找到这一点,我希望它可以帮助别人。

票数 5
EN

Stack Overflow用户

发布于 2008-12-09 15:41:02

由于DateTimeToString ( FormatDateTime调用的)使用GetThreadLocale,您可能希望为每个线程设置一个本地FormatSettings变量,甚至可以在循环之前在局部变量中设置FormatSettings。

它也可能是导致这种情况的WT_EXECUTEINTIMERTHREAD参数。请注意,它声明它只应用于非常短的任务。

如果问题仍然存在,问题可能实际上是在其他地方,这是我第一次看到这个问题,但我没有足够的信息来确定代码所做的事情。

有关访问冲突发生位置的详细信息可能会有所帮助。

票数 1
EN

Stack Overflow用户

发布于 2008-12-09 16:10:10

您确定这实际上与FormatDateTime有关吗?你提到没有作业说明,这是你问题的一个重要方面吗?如果您调用其他字符串返回函数,会发生什么情况?(确保它不是一个常量字符串。)编写自己的函数,在返回之前调用UniqueString(Result)。)

FormatSettings变量线程是特定的吗?这就是为FormatDateTime设置额外参数的意义,因此每个线程都有自己的私有副本,保证在函数活动时不会被任何其他线程修改。

计时器队列对此问题是否重要?或者,当您使用一个普通的旧TThread并在Execute方法中运行您的循环时,会得到相同的结果吗?

你的确警告说,这是一个很长的问题,但似乎你可以做一些事情来缩小它的规模,缩小问题的范围。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/352543

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档