4

This is a bit of a long question, but here we go. There is a version of FormatDateTime that is said to be thread safe in that you use

GetLocaleFormatSettings(3081, FormatSettings); 

to get a value and then you can use it like so;

FormatDateTime('yyyy', 0, FormatSettings); 

Now imagine two timers, one using TTimer (interval say 1000ms) and then another timer created like so (10ms interval);

CreateTimerQueueTimer
(
  FQueueTimer, 
  0, 
  TimerCallback, 
  nil, 
  10, 
  10, 
  WT_EXECUTEINTIMERTHREAD
);

Now the narly bit, if in the call back and also the timer event you have the following code;

for i := 1 to 10000 do
begin
  FormatDateTime('yyyy', 0, FormatSettings);
end;

Note there is no assignment. This produces access violations almost immediatley, sometimes 20 minutes later, whatever, at random places. Now if you write that code in C++Builder it never crashes. The header conversion we are using is the JEDI JwaXXXX ones. Even if we put locks in the Delphi version around the code, it only delays the inevitable. We've looked at the original C header files and it all looks good, is there some different way that C++ uses the Delphi runtime? The thread safe version of FormatDatTime looks to be re-entrant. Any ideas or thoughts from anyone who may have seen this before.

UPDATE:

To narrow this down a bit, FormatSettings is passed in as a const, so does it matter if they use the same copy (as it turns out passing a local version within the function call yeilds the same problem)? Also the version of FormatDateTime that takes the FormatSettings doesn't call GetThreadLocale, because it already has the Locale information in the FormatSettings structure (I double checked by stepping through the code).

I made mention of no assignment to make it clear that no shared storage is being accessed, so no locking is required.

WT_EXECUTEINTIMERTHREAD is used to simplify the problem. I was under the impression you should only use it for very short tasks because it may mean it'll miss the next interval if it is running something long?

If you use a plain old TThread the problem doesn't occur. What I am getting at here I suppose is that using a TThread or a TTimer works but using a thread created outside the VCL doesn't, that's why I asked if there was a difference in the way C++ Builder uses the VCL/Delphi RTL.

As an aside this code as mentioned before also fails (but takes longer), after a while, CS := TCriticalSection.Create;

  CS.Acquire;
  for i := 1 to LoopCount do
  begin
    FormatDateTime('yyyy', 0, FormatSettings);
  end;
  CS.Release;

And now for the bit I really don't understand, I wrote this as suggested;

function ReturnAString: string;
begin
  Result := 'Test';
  UniqueString(Result);
end;

and then inside each type of timer the code is;

  for i := 1 to 10000 do
  begin
    ReturnAString;
  end;

This causes the same kinds of failiures, as I said before the fault is never in the same place inside the CPU window etc. Sometimes it's an access violation and sometimes it might be an invalid pointer operation. I am using Delphi 2009 btw.

UPDATE 2:

Roddy (below) points out the Ontimer event (and unfortunately also Winsock, i.e. TClientSocket) use the windows message pump (as an aside it would be nice to have some nice Winsock2 components using IOCP and Overlapping IO), hence the push to get away from it. However does anyone know how to see what sort of thread local storage is setup on the CreateQueueTimerQueue?

Thanks for taking the time to think and answer this problem.

4

8 に答える 8

5

自分の質問に「回答」を投稿するのが適切かどうかはわかりませんが、論理的に思えました。それがクールでない場合はお知らせください。

私は問題を発見したと思います.スレッドローカルストレージのアイデアは、私を一連のリードに従うように導き、この魔法のラインを見つけました;

IsMultiThread := 真;

ヘルプから;

「IsMultiThread は true に設定され、メモリ マネージャーが複数のスレッドをサポートする必要があることを示します。IsMultiThread は、BeginThread およびクラス ファクトリによって true に設定されます。」

これはもちろん、TTimer を使用して単一のメイン VCL スレッドを使用して設定されるわけではありませんが、TThread を使用すると設定されます。手動で設定すると、問題はなくなります。

C++Builder では TThread を使用しませんが、次のコードを使用すると表示されます。

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

つまり、自動的にどこかに設定されます。

私はこれを見つけることができたので、人々の意見を本当にうれしく思っています.

于 2008-12-10T01:34:34.660 に答える
1

FormatDateTime が呼び出す DateTimeToString は GetThreadLocale を使用するため、各スレッドにローカルの FormatSettings 変数を設定してみてください。ループの前にローカル変数に FormatSettings を設定することもできます。

これは、WT_EXECUTEINTIMERTHREAD パラメータが原因である可能性もあります。非常に短いタスクにのみ使用する必要があると記載されていることに注意してください。

問題が解決しない場合、問題は実際には別の場所にある可能性があります。これは、これを見たときの私の最初の予感でしたが、コードが実際にそれを判断するのに十分な情報がありません。

アクセス違反が発生した場所に関する詳細が役立つ場合があります。

于 2008-12-09T15:41:02.207 に答える
1

これは実際に何か関係がありFormatDateTimeますか?そこには割り当てステートメントがないことを指摘しました。それはあなたの質問の重要な側面ですか?代わりに他の文字列を返す関数を呼び出すとどうなりますか? (定数文字列でないことを確認してください。UniqueString(Result)戻る前に呼び出す独自の関数を作成してください。)

FormatSettings変数はスレッド固有ですか? これが の追加パラメーターを持つポイントであるFormatDateTimeため、各スレッドには、関数がアクティブな間は他のスレッドによって変更されないことが保証されている独自のプライベート コピーがあります。

タイマー キューはこの質問にとって重要ですか? それとも、単純な古いものを使用してメソッドTThreadでループを実行すると、同じ結果が得られますか?Execute

あなたはそれが長い質問であると警告しましたが、問題の範囲を絞り込むために、質問を小さくするためにできることがいくつかあるようです.

于 2008-12-09T16:10:10.587 に答える
0

あなたはあなたの答えを見つけました-IsMultiThread。これは、APIの使用に戻り、スレッドを作成するためにいつでも使用する必要があります。MSDNから:CreateTimerQueueTimerは、この機能を処理するためのスレッドプールを作成しているため、保護なしでメインVCLスレッドと連携する外部スレッドがあります。(注:コードの他の部分がこのロックを尊重しない限り、CS.acquire / releaseは何もしません。)

于 2008-12-10T03:17:12.550 に答える
0

update2 の場合:

無料の IOCP ソケット コンポーネントがあります: http://www.torry.net/authorsmore.php?id=7131 (ソース コードが含まれています)

「Naberegnyh Sergey N 著..Windows Completion Port に基づき、Windows Socket Extensions を使用した高性能ソケット サーバー。IPv6 をサポート。」

私の小さなインスタントメッセージングサーバーを再構築するためのより良いコンポーネント/ライブラリを探しているときに見つけました。まだ試していませんが、第一印象としてはうまくコーディングされているようです。

于 2009-01-13T00:38:22.787 に答える
0

再。Winsock と重複する I/O に関する最後の質問: Indyをよく見る必要があります。

Indy はブロッキング I/O を使用するため、メイン スレッドが何を行っているかに関係なく、高性能のネットワーク IO が必要な場合に最適です。マルチスレッドの問題を解決したので、別のスレッド (またはそれ以上) を作成して、indy を使用して I/O を処理する必要があります。

于 2008-12-10T08:50:43.330 に答える
0

Indy の問題は、多くの接続が必要な場合、まったく効率的ではないことです。接続ごとに 1 つのスレッド (ブロッキング I/O) が必要であり、まったくスケーリングされないため、IOCP とオーバーラップ IO の利点が得られます。これは、Windows でほぼ唯一のスケーラブルな方法です。

于 2008-12-10T10:17:17.790 に答える
0

あなたが作成している RTL/VCL 呼び出しは、タイマー キューを介してコードを呼び出すときに、正しく設定されていないスレッド ローカル ストレージ (TLS) 変数へのアクセスを予期しているのではないでしょうか?

これはあなたの問題に対する答えではありませんが、TTimer OnTimer イベントがメイン VCL スレッドの通常のメッセージ ループの一部として実行されることをご存知ですか?

于 2008-12-10T00:00:51.013 に答える