2

我有一个带有这些代码的服务器:

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;
4

4 回答 4

2

每个访问数据库的线程都应该有自己的连接,您不能在多个线程之间共享一个数据库连接。OnExecute 事件是在请求客户端对应的线程上下文中调用的,所以每次调用都是在一个工作线程内部执行的,这样的线程应该有自己的 DB 连接。

如果不想为每个工作线程建立新的连接;一种选择可能是,您将一个线程专用于 DB 连接,并将所有 DB 操作委托给该线程,例如,您的其他线程可以将其 INSERT SQL 语句发送到该 DB 线程中的队列,并且该 DB 线程执行它们一个-使用单个数据库连接。当然,如果你采用这种方法,所有的数据库负载都将在一个线程上,如果你有这么多的数据库操作,那么那个数据库线程本身就可能成为性能瓶颈!更重要的是,采用这种方法,查询执行将是异步的,除非您在每个线程要求 DB 线程为它们执行 DB 查询时使用同步技术。

另请注意,如果您的数据库访问组件是 ADO,那么您必须调用 CoInitialize 和 CoUninitialize,因为 Delphi 运行时仅对主线程执行此操作,而不是您创建的其他线程。

于 2010-08-21T13:00:54.767 回答
2

是和不是。您可以从不同的线程访问单个表,但您需要每个线程都有一个 TSQLConnection 实例才能安全地访问。

更新

为每个线程实例化不同的连接很好。这也是大多数网页一直在做的事情(使用 asp、php 或 ... 的服务器端脚本意味着无状态执行,因此连接通常无法生存到下一个请求,必须重新建立)。

如果您担心开销,可以考虑使用像 vcldeveloper 建议的单个连接。您必须确保由其他线程更改的“连接线程”使用的任何变量和成员字段(例如接收要执行的 SQL 的字段成员)必须受到某种同步机制的保护.

这同样适用于 mjustin 建议的连接池,但在这种情况下,连接池需要通过同步机制进行保护。

于 2010-08-21T11:18:44.190 回答
2

我会为数据库连接使用连接池。然后,每个线程仅在需要时从池中请求连接(如果池中当前没有空闲连接,则可能会阻塞),然后使用并最终将其返回到池中。池的优点是所需的连接数少于并发线程数,并且在需要时连接已经存在。

于 2010-08-21T14:40:01.167 回答
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;
  ...

如果您想拥有一个数据库连接池,这非常棘手,因为连接通常是特定于线程的 - 每个线程都需要一个,并且不能在线程之间传递它们。所以你最终会写很多代码来支持它。

我现在使用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;

如果您编写自己的线程或连接池,您将需要做类似的事情。

于 2010-08-23T01:19:08.587 回答