2008-12-09 53 views
4

这是一个很长的问题,但我们走了。还有一个是被说成是线程安全的,这与使用Delphi Win API CreateTimerQueueTimer线程和线程安全FormatDateTime崩溃

GetLocaleFormatSettings(3081, FormatSettings); 

得到一个值,然后你可以使用它像这样的FormatDateTime的版本;

FormatDateTime('yyyy', 0, FormatSettings); 

现在想象两个定时器,使用TTimer酮(间隔说1000毫秒),然后,像这样(10ms的时间间隔)中创建另一定时器;

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传递作为一个常量,所以它的问题,如果他们使用相同的副本(因为它原来传递函数调用中的本地版本yeilds同样的问题)?此外,使用FormatSettings的FormatDateTime版本不会调用GetThreadLocale,因为它已经在FormatSettings结构中具有Locale信息(我通过逐步检查代码进行了双重检查)。

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

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; 

这将导致同种failiures的,正如我之前所说的故障是从未在CPU窗口等内同一个地方有时候这是一个访问冲突,有时它可能是一个无效的指针操作。我使用Delphi 2009 btw。

更新2:

罗迪(下)指出了OnTimer事件(不幸的是还Winsock的,即Tclientsocket构件实现)使用Windows消息泵(顺便说一句这将是很好,有一些很好的Winsock2组件使用IOCP和重叠IO),因此推动摆脱它。然而,有谁知道如何查看CreateQueueTimerQueue上设置的线程本地存储的种类?

感谢您花时间思考和解答此问题。

回答

5

我不确定发布“回答”给我自己的问题是否是好的形式,但它似乎是合乎逻辑的,让我知道如果这是不酷的。

我想我已经找到了问题,线程本地存储的想法导致我跟随一堆线索,我发现这个神奇的线;

IsMultiThread:= True;

从帮助;

“IsMultiThread设置为true,表明内存管理器应该支持多线程,而BeginMatadata和类工厂将IsMultiThread设置为true。

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

在C++ Builder中,我不使用TThread,但它使用下面的代码出现;

if (IsMultiThread) { 
    ShowMessage("IsMultiThread is True!"); 
} 

这是它为你自动设置的地方。

我真的很高兴人们的投入,所以我可以找到这个,我希望徒然地可能会帮助别人。

+0

如果你创建一个标准的Delphi线程,那么就没有必要设置这个值。但有时候,就像在一个称为多线程的DLL中一样,你必须自己设置它。好点。 – mj2008 2009-01-13 09:09:55

+0

帮助我,欢呼! – Blorgbeard 2009-11-30 02:24:35

1

FormatDateTime调用的DateTimeToString使用GetThreadLocale,您可能希望尝试为每个线程使用本地FormatSettings变量,甚至可以在循环之前在本地变量中设置FormatSettings。

它也可能是WT_EXECUTEINTIMERTHREAD参数引起的。请注意,它声明它只应用于非常短的任务。

如果问题仍然存在,问题可能实际上存在于其他地方,这是我第一次看到这一点时的预感,但我没有足够的信息来了解代码如何确定。

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

1

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

FormatSettings变量是线程特定的吗?这就是拥有FormatDateTime的额外参数的要点,因此每个线程都有自己的专用副本,保证在函数处于活动状态时不会被其他任何线程修改。

计时器队列对于这个问题很重要吗?或者当你使用普通的旧版本TThread并在Execute方法中运行循环时,你会得到相同的结果吗?

你确实警告说这是一个长期的问题,但似乎有一些事情可以使它变小,以缩小问题的范围。

0

我想知道您正在创建的RTL/VCL调用是否期望访问某些线程本地存储(TLS)变量,而这些变量在您通过计时器队列调用代码时没有正确设置?

这不是您的问题的答案,但您是否知道TTimer OnTimer事件只是作为主VCL线程中正常消息循环的一部分运行?

0

你找到了你的答案 - IsMultiThread。这必须随时用于恢复使用API​​并创建线程。从MSDN:CreateTimerQueueTimer正在创建一个线程池来处理这个功能,所以你有一个外部线程与主VCL线程一起工作,没有任何保护。 (注意:你的CS.acquire/release完全不做任何事,除非代码的其他部分尊重这个锁。)

+0

我不清楚为什么“CS。获取/释放不会做任何事情“,因为我原本以为它会在两个地方保护该代码FormatDateTime,但我认为主要VCL在其他地方使用内存管理器,我无法轻松控制并导致问题。 – Bruce 2008-12-10 04:17:34

0

Re。你最后一个关于Winsock和重叠I/O的问题:你应该仔细看看Indy

Indy使用阻塞I/O,无论主线程在做什么,都需要高性能的网络IO,这是一个很好的选择。现在你已经破解了多线程问题,你应该创建另一个线程(或更多)来使用indy来处理你的I/O。

0

Indy的问题是,如果你需要很多连接,它根本就不是高效的。它每个连接都需要一个线程(阻塞I/O),因此IOCP和Overlapping IO的好处几乎无法扩展,这几乎是Windows上唯一可扩展的方式。

0

对于UPDATE2:

有一个免费的完成端口插座组件(包括源代码)http://www.torry.net/authorsmore.php?id=7131

“通过Naberegnyh谢尔盖ñ。高 性能socket服务器基于 的Windows完成端口和使用 Windows套接字扩展支持IPv6 “

我已经fou一边寻找更好的组件/库来重建我的小型即时通讯服务器。我还没有尝试过,但它看起来不错,作为第一印象。