OneXecuteイベント(Indy)でデータベースを操作する
-
30-09-2019 - |
質問
これらのコードを備えたサーバーがあります。
procedure TFrmMain.TCPServerExecute(AContext: TIdContext);
begin
Res := DoRegister(Name,Family,Username,Password);
end;
function TFrmMain.DoRegister(Name,Family,Username,Password:string): bool;
var
Qry: TSQLQuery;
begin
Qry := TSQLQuery.Create(nil);
try
Qry.SQLConnection := FrmConnect.SQLConnection;
Qry.SQL.Text :='INSERT INTO `table` ...';
Qry.ExecSQL();
finally
Qry.Free;
end;
Result := True;
end;
さまざまなスレッドで1つのテーブルへのアクセスに問題はありますか?そして、OneXecuteイベントで使用されるのは何が危険なことですか?
応答の友達をありがとう。
それで、それは異なるスレッドに対して異なる接続を行う本当の方法ですか?
var
Qry: TSQLQuery;
SqlCon: TSQLConnection;
Begin
SqlCon := TSQLConnection.Create(nil);
Qry := TSQLQuery.Create(nil);
try
SqlCon := FrmConnect.SQLConnection;
Qry.SQLConnection := SqlCon;
finally
SqlCon.Free;
Qry.Free;
end;
end;
解決
2番目のコードフラグメントは正しくありません。接続文字列をコピーする必要がある場合、グローバル接続との新しい接続を上書きしています。また、あなたはおそらくあなたのアプリケーションの残りの部分に問題を引き起こすグローバルを解放しています。 TSQLConnectionクラスの詳細に応じて、このようなもの:
SqlCon := TSQLConnection.Create(nil); // create
Qry := TSQLQuery.Create(nil);
try
//SqlCon := FrmConnect.SQLConnection; // overwrite!!!
SqlCon.ConnectionString := FrmConnect.SQLConnection.ConnectionString;
SqlCon.Active := true;
Qry.SQLConnection := SqlCon;
...
DataBse接続プールを持ちたい場合は、接続が通常スレッド固有であるため、非常に注意が必要です。スレッドごとに1つが必要で、スレッド間で渡すことはできません。そのため、それをサポートするために多くのコードを書くことになります。
私は今使用します OmnithReadLibrary また、新しいデータベース接続を返す工場メソッドがあります。これにより、タスクに供給されるスレッドプールが得られるため、特定のタスクは実行時に既存のスレッドにバインドされますが、スレッドはかなり長寿命です。これを取得するために書かなければならなかったコードは非常に小さいです(私はADOを使用しています):
type
// a factory to generate new instances of our thread-specific data
IThreadPoolData = interface
['{14917B01-6613-4737-B87E-0046789D4284}']
function GetConnection: TADOConnection;
function GetStoredProc: TADOStoredProc;
end;
TThreadPoolData = class(TInterfacedObject, IThreadPoolData)
strict private
FADOConnection: TADOConnection;
FStoredProc: TADOStoredProc; // lazy creation!
public
constructor Create(aConnectionString: string); overload;
destructor Destroy; override;
function GetConnection: TADOConnection;
function GetStoredProc: TADOStoredProc;
end;
// create the connection here so thread creation is slow but using it
// is (relatively) fast
constructor TThreadPoolData.Create(aConnectionString: string);
begin
FADOConnection := TADOConnection.Create(nil);
FADOConnection.LoginPrompt := false;
FADOConnection.ConnectionString := aConnectionString;
FADOConnection.ConnectOptions := coAsyncConnect;
FADOConnection.Connected := true;
end;
destructor TThreadPoolData.Destroy;
begin
FADOConnection.Connected := false;
if assigned(FStoredProc) then
FreeAndNil(FStoredProc);
FreeAndNil(FADOConnection);
end;
独自のスレッドまたは接続プールを書く場合、同様のことをする必要があります。
他のヒント
DBにアクセスする各スレッドには独自の接続が必要です。複数のスレッド間でDB接続を共有することはできません。 OneXecuteイベントは、要求クライアントに対応するスレッドのコンテキストで呼び出されます。そのため、呼び出されるたびにワーカースレッド内で実行され、そのようなスレッドには独自のDB接続が必要です。
各ワーカースレッドの新しい接続を確立したくない場合。 1つのオプションは、DB接続に単一のスレッドを捧げ、すべてのDB操作をそのスレッドに委任することです。たとえば、他のスレッドでは、そのDBスレッドのキューに挿入SQLステートメントを送信でき、DBスレッドは1つを実行できます。単一のdb接続を使用してバイワン。もちろん、このアプローチをとると、すべてのDB負荷が単一のスレッドにあり、DB操作が非常に多い場合、そのDBスレッド自体がパフォーマンスボトルネックになる可能性があります。さらに、このアプローチをとると、クエリ実行は、各スレッドがDBスレッドにDBクエリを実行するように依頼するたびに同期テクニックを使用することを除いて、非同期です。
また、DBアクセスコンポーネントがADOである場合は、CoinitializeとCounitializeを呼び出す必要があることに注意してください。Delphiランタイムは、メインスレッドに対してのみ、あなたが作成する他のスレッドではないためです。
データベース接続に接続プールを使用します。すべてのスレッドは、必要に応じてプールから接続を要求します(プールに自由接続が現在ない場合はブロックされる可能性があります)。その後、最終的にプールに戻します。プールには、同時スレッドがあるよりも必要な接続が少なく、必要に応じて接続が既に存在するという利点があります。
はいといいえ。さまざまなスレッドから単一のテーブルにアクセスできますが、安全に行うには、スレッドごとにTSQLConnectionインスタンスが必要です。
アップデート
スレッドごとに異なる接続をインスタンス化することは問題ありません。これは、ほとんどのWebページも常に行っていることです(ASP、PHPまたは...を使用してサーバーサイドスクリプトを意味するため、接続は通常、次の要求に耐えられず、再確立する必要があります)。
オーバーヘッドについて心配している場合は、VCLDeveloperが提案するような単一の接続の使用を検討できます。他のスレッドによって変更される「接続スレッド」で使用される変数とメンバーフィールド(たとえば、実行するSQLを受信するフィールドメンバー)が、何らかの同期メカニズムによって保護される必要があることを確認する必要があります。 。
Mjustinが提案したように接続プールにも同じことが当てはまりますが、その場合、接続プールは同期メカニズムによって保護する必要があります。