【问题标题】:Working with database in the OnExecute event (Indy)在 OnExecute 事件中使用数据库 (Indy)
【发布时间】:2011-04-02 00:29:13
【问题描述】:

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

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;

【问题讨论】:

    标签: multithreading delphi indy dbconnection


    【解决方案1】:

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

    更新

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

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

        • 如果有几个线程,是否可以为每个线程创建并保持一个连接?
        【解决方案4】:

        您的第二个代码片段不正确。当您应该复制连接字符串时,您正在用全局连接覆盖新连接。您还释放了可能会导致应用程序其余部分出现问题的全局变量。像这样,取决于你的 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;
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-07-27
          • 2015-02-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-07
          相关资源
          最近更新 更多