Работа с базой данных в событии OnExecute (Indy) - PullRequest
2 голосов
/ 21 августа 2010

У меня есть сервер с этими кодами:

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 ]

2 голосов
/ 23 августа 2010

Ваш второй фрагмент кода неверен.Вы перезаписываете новое соединение глобальным соединением, когда вы должны копировать строку соединения.Вы также освобождаете тот глобальный объект, который, вероятно, вызовет проблемы для остальной части вашего приложения.Примерно так, в зависимости от деталей вашего класса 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;

Вам нужно будет сделать нечто подобное, если вы напишите свой собственный поток или пул соединений.

2 голосов
/ 21 августа 2010

Каждый поток, обращающийся к БД, должен иметь свое собственное соединение, вы не можете разделить соединение с БД между несколькими потоками. Событие OnExecute вызывается в контексте потока, соответствующего запрашивающему клиенту, поэтому каждый раз, когда он вызывается, он выполняется внутри рабочего потока, и такой поток должен иметь свое собственное соединение с БД.

Если вы не хотите устанавливать новое соединение для каждого рабочего потока; Одним из вариантов может быть то, что вы выделяете один поток для подключения к БД и делегируете все операции с БД этому потоку, например, ваши другие потоки могут отправлять свои операторы INSERT SQL в очередь в этом потоке БД, и этот поток БД выполняет их один - по одному, используя одно соединение с БД. Конечно, если вы воспользуетесь этим подходом, вся нагрузка на БД будет осуществляться в одном потоке, а если у вас так много операций с БД, то сам этот поток БД может стать узким местом для производительности! Более того, при таком подходе выполнение запросов будет асинхронным, за исключением того, что вы используете технику синхронизации всякий раз, когда каждый из ваших потоков просит поток БД выполнить для них запрос БД.

Также обратите внимание, что если ваши компоненты доступа к БД являются ADO, то вы должны вызывать CoInitialize и CoUninitialize, поскольку среда выполнения Delphi делает это только для основного потока, а не для других потоков, созданных вами.

2 голосов
/ 21 августа 2010

Я бы использовал пул соединений для соединений с базой данных.Затем каждый поток запрашивает соединение из пула только тогда, когда это необходимо (что может блокировать, если в данный момент в пуле нет свободных соединений), а затем использует и, наконец, возвращает его в пул.Преимущество пула состоит в том, что требуется меньше соединений, чем одновременных потоков, а соединения уже присутствуют при необходимости.

2 голосов
/ 21 августа 2010

Да и нет.Вы можете получить доступ к одной таблице из разных потоков, но вам нужен экземпляр TSQLConnection для каждого потока, чтобы сделать это безопасно.

Обновление

Установка отдельного соединения для каждого потокахорошо.Это то, что делает большинство веб-страниц все время (сценарии на стороне сервера с использованием asp, php или ... означают выполнение без состояния, и поэтому соединения обычно не сохраняются до следующего запроса и должны быть восстановлены).

Если вы беспокоитесь о накладных расходах, вы можете рассмотреть возможность использования одного соединения, как предлагает vcldeveloper.Вы должны будете убедиться, что любые переменные и поля-члены, используемые этим «потоком соединения», которые изменены другими потоками (например, член поля, получающий SQL для выполнения), будут защищены каким-то механизмом синхронизации.

То же самое относится к пулу соединений, как предложено mjustin, хотя в этом случае пул соединений должен быть защищен механизмами синхронизации.

...