在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;
在各个线程中访问一个表是否有问题?完全可以在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;
解决方案
您的第二个代码片段不正确。当您应该复制连接字符串时,您将使用全局连接覆盖新连接。您还可以释放该全球,这可能会给您的其余应用带来问题。像这样的东西,取决于您的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;
...
如果您想拥有一个数据库连接池,那非常棘手,因为连接通常是特定于线程的 - 每个线程需要一个,并且不能在线程之间传递它们。因此,您最终编写了很多代码来支持这一点。
我现在使用 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连接。
如果您不想建立每个工作线程的新连接;一个选项可能是,您将单个线程用于DB连接,并将所有DB操作委派给该线程,例如,您的其他线程可以将其插入SQL语句发送到该DB线程中的队列,并且DB线程将它们执行 - 使用单个DB连接。当然,如果您采用这种方法,所有DB负载都将在一个线程上,并且如果您拥有如此多的DB操作,那么DB线程本身可能是性能瓶颈!更重要的是,采用这种方法,查询执行将是异步的,除非您只要每个线程要求DB线程为它们执行DB查询,否则您会使用同步技术。
还要注意,如果您的DB访问组件是ADO,则必须调用Conitialize和Counitialialize,因为Delphi运行时只能对主线程而不是由您创建的其他线程来执行此操作。
我将使用连接池进行数据库连接。然后,每个线程仅在需要时从池中请求连接(如果池中没有自由连接,可能会阻止它),然后使用并最终将其返回到池中。池的优势是,所需的连接比并发线程少,并且在需要时已经存在连接。
是的,否。您可以从不同的线程访问单个表,但是每个线程需要一个TSQLConnection实例才能安全地进行操作。
更新
实例化每个线程的不同连接是可以的。这也是大多数网页也一直在做的事情(使用ASP,PHP或...表示无国经执行的服务器端脚本,因此连接通常无法在下一个请求中生存,并且必须重新建立)。
如果您担心开销,可以考虑使用像VCLDEVEVER建议的单个连接。您将必须确保该“连接线程”使用的任何变量和成员字段由其他线程更改(例如,接收要执行的SQL的字段成员)必须受某种同步机制保护。
如Mjustin所建议的那样,它也适用于连接池,尽管在这种情况下,连接池需要受同步机制保护。