我有一台具有这些代码的服务器:

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所建议的那样,它也适用于连接池,尽管在这种情况下,连接池需要受同步机制保护。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top