2010-08-21 18 views
2

我有這些代碼的服務器:在OnExecute事件(印)使用數據庫

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; 

回答

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

如果你想有一個DATABSE連接池是相當棘手,因爲連接通常是線程特定的 - 你需要每個線程之一,你可以」在線程之間傳遞它們。所以你最終編寫了很多代碼來支持它。

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

你需要的,如果你寫你自己的線程或連接池做同樣的事情。

+0

謝謝你的迴應。 – Kermia 2010-08-23 06:56:31

2

是和否。您可以從不同線程訪問單個表,但每個線程需要一個TSQLConnection實例才能安全地執行此操作。

更新

實例化的每個線程不同的連接是好的。這是大多數網頁一直都在做的事情(使用asp,php或...的服務器端腳本意味着無狀態執行,因此連接通常無法生存到下一個請求並且必須重新建立)。

如果您擔心開銷,可以考慮使用像vcldeveloper建議的單個連接。您必須確保由其他線程更改的「連接線程」使用的任何變量和成員字段(例如接收要執行的SQL的字段成員)必須受某種同步機制保護。

這同樣適用於mjustin所建議的連接池,但在這種情況下,連接池需要通過同步機制進行保護。

+0

謝謝,請再看看我的問題。 (已編輯) – Kermia 2010-08-21 15:53:00

2

訪問數據庫的每個線程都應該有自己的連接,您不能在多個線程之間共享數據庫連接。 OnExecute事件是在與請求客戶端相對應的線程的上下文中調用的,因此每次調用時都會在工作線程內執行,並且此類線程應該有自己的數據庫連接。

如果您不想爲每個工作線程建立新的連接;一種選擇可能是,爲數據庫連接分配一個單獨的線程,並將所有數據庫操作委託給該線程,例如,其他線程可以將其INSERT SQL語句發送到該數據庫線程中的隊列,並且該數據庫線程單向執行它們,使用單個數據庫連接進行一對一的連接。當然,如果採取這種方法,所有數據庫負載都將位於單個線程中,並且如果您有如此多的數據庫操作,那麼該數據庫線程本身可能會成爲性能瓶頸!更重要的是,採用這種方法時,查詢執行將是異步的,除非每個線程都要求數據庫線程爲它們執行數據庫查詢時使用同步技術。

另請注意,如果您的數據庫訪問組件是ADO,那麼您必須調用CoInitialize和CoUninitialize,因爲Delphi運行時只會爲主線程執行該操作,而不會執行由您創建的其他線程。

2

我會使用連接池進行數據庫連接。每個線程只在需要時才從池中請求連接(這可能會阻止池中當前沒有空閒連接),然後使用並最終將其返回給池。池具有的優點是,所需的連接數少於併發線程數,並且連接在需要時已經存在。

+0

如果有幾個線程可以爲每個線程創建並保持連接嗎? – SAMPro 2015-03-02 07:20:34

相關問題