在 OnExecute 事件中使用数据库 (Indy)

发布于 2024-09-15 13:38:28 字数 871 浏览 11 评论 0原文

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

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;

i have a server with these codes :

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;

is there any problem with access to one table in various Threads ? and totally what is dangerous to be used in the Onexecute event ?

Thank you for response friends .

so , is it a true way to make different connections for different threads ?

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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凉世弥音 2024-09-22 13:38:28

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

如果您编写自己的线程或连接池,则需要执行类似的操作。

Your second code fragment is not correct. You're overwriting the new connection with the global connect, when you should be copying out the connection string. You're also freeing that global which will probably cause problems for the rest of your application. Something like this, depending on the details of your TSQLConnection class:

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

If you want to have a databse connection pool it's quite tricky because the connections are usually thread-specific - you need one per thread and you can't pass them between threads. So you end up writing a lot of code to support that.

I now use the OmniThreadLibrary and have a factory method that returns a new database connection. That gives me a thread pool that I feed tasks into, so my specific task is bound to an existing thread when it executes, but the thread is fairly long-lived. The code I had to write to get this is very small (I'm using 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;

You will need to do something similar if you write your own thread or connection pool.

彩扇题诗 2024-09-22 13:38:28

每个访问数据库的线程都应该有自己的连接,不能在多个线程之间共享数据库连接。 OnExecute 事件是在与请求客户端对应的线程上下文中调用的,因此每次调用它时,都会在工作线程内执行,并且这样的线程应该有自己的数据库连接。

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

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

Each thread accessing DB should have its own connection, you cannot share a DB connection between several threads. OnExecute event is invoked in the context of the thread corresponding to the requesting client, so each time it is invoked, it is executed inside a worker thread, and such a thread should have its own DB connection.

If you do not want to establish a new connection for each worker thread; one option could be, you dedicate a single thread for DB connection, and delegate all DB operations to that thread, for example your other threads can send their INSERT SQL statements to a queue in that DB thread, and that DB thread executes them one-by-one using a single DB connection. Of course if you take this approach, all DB load would be on a single thread, and if you have so many DB operations, then that DB thread itself could be a performance bottleneck! What's more, taking this approach, query executions would be asynchronous except you use a synchronization technique whenever each of your threads ask the DB thread to execute a DB query for them.

Also take note that if your DB access components are ADO, then you have to call CoInitialize and CoUninitialize, because Delphi runtime only does that for the main thread not other threads which are created by you.

生生漫 2024-09-22 13:38:28

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

I would use a connection pool for the database connections. Every thread then only requests a connection from the pool when needed (which might block if there are currently no free connections in the pool) and then uses and finally returns it to the pool. A pool has the advantage that there are less connections needed than there are concurrent threads, and the connections are already present when needed.

守不住的情 2024-09-22 13:38:28

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

更新

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

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

这同样适用于 mjustin 建议的连接池,尽管在这种情况下,连接池需要受到同步机制的保护。

Yes and no. You can access a single table from different threads, but you need a TSQLConnection instance per thread to do so safely.

Update

Instantiating a different connection for each thread is fine. It's what most webpages do all the time as well (server side scripting using asp, php or ... means state-less execution and thus connections usually do not survive to the next request and have to be re-established).

If you are worried about the overhead, you can consider using a single connection like vcldeveloper suggests. You will have to ensure that any variables and member fields used by that "connection thread" which are changed by the other threads (for example the field member receiving the SQL to be executed), will have to be protected by some sort of synchronisation mechanism.

The same applies to the connection pool as suggested by mjustin, though in that case, the connection pool needs to be protected by synchronisation mechanisms.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文