Фоновое выполнение и синхронизация потоков

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Фоновое выполнение и синхронизация потоков

Сообщение ssl » 25.06.2013 12:19:15

Перевожу наработки с Дельфи на Лазарус, с винды под линукс.
Есть у меня свой DataSet, который умеет всё общение с сервером БД выполнять в фоне, в отдельной нити. Ну, что б юзер, ожидая результат, мог хотя бы окно передвинуть. Сейчас (под windows реализовано) просто: есть отдельная нить (thread), который ждет задание. Главная нить готовит задание, освобождает рабочую нить и ждёт окончание выполнения (TEvent), используя MsgWaitForMultipleObjects. Если в очередь поступило сообщение, вызывается событие компонента, на который разработчик приложения вешает Application.ProcessMessages.

Псевдокод:
Код: Выделить всё
procedure THtDataSet.CreateCursor;
var rc: integer;
begin
  .........
  FTask:= PrepareTask(FSql.Text); // Задание для Thread
  FEvJob.SetEvent; // рабочий Thread ожидает этот Event
  repeat
    rc:= MsgWaitForMultipleObjects(3, Hadles, false, 150, QS_ALLINPUT);
    case rc of
        WAIT_OBJECT_0  : DoHandleCallBack;  // call back signaled
        WAIT_OBJECT_0+1,  // completed
        WAIT_OBJECT_0+2: break;  // thread terminated
        WAIT_OBJECT_0+3: HandleMessages;    // message
        WAIT_TIMEOUT   : HandleIdle;  // every 150 ms
    end;
  until false;
  .............
end;


Как бы подобное выкрутить в линуксе? Если оставить этот алгоритм, то нужен какой-нибудь сигнал, что пора вызывать ProcessMessages, и средство ожидания, аналогичное MsgWaitForMultipleObjects. Или в lin есть свои способы?

Вот только чего не хотелось бы, так это перекладывать управление многопоточностью на разработчика приложения. В Win вот удалось; достаточно в свойстве компонента выбрать режим работы (синхронный/асинхронный), весь механизм уже "у ей внутре" (с).

Добавлено спустя 3 часа 19 минут 31 секунду:
Нашел "condition variable", теперь понятно (хоть и не привычно), как ждать "любой из", но как при этом ждать появление события от пользователя (аналог Message в Win)??
ssl
новенький
 
Сообщения: 59
Зарегистрирован: 17.05.2005 11:27:01

Re: Фоновое выполнение и синхронизация потоков

Сообщение iN0k » 25.06.2013 22:42:11

Тут идеиалогическая проблемма. Чтобы обеспечить "плавность интерфейса" в ГЛАВНОМ потоке необходимо избавиться от вызовов типа sleep и wait.. и любых "бесконечных"-ожидающих циклов.

Тоесть ваш подход, при некоторых обстоятельствах, конечно же уменьшит фризы, но совершенно не решает проблем му.

Я бы использовал "событийную" модель. Тоесть доп.поток получает задание и выполняет его, после чего, посредством threаd.synhronaiz выполняет обновление визуальной части.
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Фоновое выполнение и синхронизация потоков

Сообщение svk12 » 25.06.2013 23:41:23

iN0k писал(а):Тоесть доп.поток получает задание и выполняет его, после чего, посредством threаd.synhronaiz выполняет обновление визуальной части.


Такой способ работает под Линухом. Проверено.
svk12
постоялец
 
Сообщения: 409
Зарегистрирован: 09.06.2008 18:42:47

Re: Фоновое выполнение и синхронизация потоков

Сообщение ssl » 26.06.2013 17:44:00

Всё верно, так и есть: поток получает задание и работает. Проблема в том, что я не должен возвращать управление из функции, которая дала потоку задание, пока он это задание не выполнит, и продолжать при этом обработку событий. Ну, привык уже прикладной программист писать
Query.Open;
While not Query.Eof do begin
...
Query.Next;
end;


Так вот Open формирует задание потоку и ждёт, пока тот закончит. И хотелось бы, что б он не просто ждал, а еще и ProcessMessages как-то отрабатывал.
А если Open ждать не будет, так это надо совсем другой набор компонент (не наследники TDataSet) с другой прадигмой использования.
ssl
новенький
 
Сообщения: 59
Зарегистрирован: 17.05.2005 11:27:01

Re: Фоновое выполнение и синхронизация потоков

Сообщение svk12 » 26.06.2013 20:04:28

ssl писал(а):Так вот Open формирует задание потоку и ждёт, пока тот закончит.

Т.е., как я понял, Query это экземпляр класса-потомка TDataset, который имеет в своём составе поле
FTask - потомок TThread для выполнения(возможно длительного) запроса к БД?
svk12
постоялец
 
Сообщения: 409
Зарегистрирован: 09.06.2008 18:42:47

Re: Фоновое выполнение и синхронизация потоков

Сообщение iN0k » 27.06.2013 07:18:00

ну ... попробуйте тогда так
Код: Выделить всё
  tMyThread=class(ttHread)
   protected
    _Complite:boolean;

   protected
     procedure Execute; override;

   public
     constructor Create;
     procedure exWaitFor;
   end;


сonstructor tMyThread.Create;
begin
    // что-то делаем
     FreeOnTerminate:=FALSE;
   _Complite:=FALSE;
end;


procedure tMyThread.Execute;
begin
    // что-то делаем
  _Complite:=TRUE; //< обязательно ПОСЛЕДНЕЙ строкой
end;

procedure tMyThread.exWaitFor;
begin
    while (NOT Terminate)or(_Complite) do begin
        Application.ProcessMessages;
        ThreadSwitch;
    end;
end;
// использование
...
t:=tMyThread.Create(TRUE,...)
t.Start;
t.exWaitFor;
t.FREE;
...


но опять же повторюсь, это НЕ решает проблему.
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Фоновое выполнение и синхронизация потоков

Сообщение ssl » 27.06.2013 13:40:48

Query это экземпляр класса-потомка TDataset, который имеет в своём составе поле
FTask - потомок TThread для выполнения(возможно длительного) запроса к БД?

Именно!

С потоками я насобачился общаться ещё под виндой, тут проблем нет. Я пока никак не могу понять, как мне ждать ИЛИ события от интерфейса, ИЛИ завершения своего задания. Нарыл в инете про ConditionVariable, как замену waitformultipleobjects, но никак не возьму в толк, как туда запихнуть ожидание Messages. Эти ConditionVariable, похоже, никак не приспособлены под работу с чужими потоками. Если в винде я могу ждать любые хэндлы (процессы, нити, события..), то тут так не получится :(

В-общем, уже понятно, что облом мне :)
ssl
новенький
 
Сообщения: 59
Зарегистрирован: 17.05.2005 11:27:01

Re: Фоновое выполнение и синхронизация потоков

Сообщение iN0k » 27.06.2013 14:45:04

эм ... яж вам показал как можно в посте выше, по идее и в линюксах должно работать.
я там не правильно выразился. вместо "но опять же повторюсь, это НЕ решает проблему." надо "но опять же повторюсь, это ПЛОХОЕ решение." :D
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Фоновое выполнение и синхронизация потоков

Сообщение ssl » 27.06.2013 16:44:46

To iN0k:

Да, спасибо, я обязательно подумаю, как тут выкрутиться. Похоже, придётся делать спец. евент у компонента, на который кодер будет должен повесить обработчик с ProcessMessages (Компоненты не обязаны знать про Application, приложение может быть любого вида). Нужны эксперименты. По результату отпишусь :)
ssl
новенький
 
Сообщения: 59
Зарегистрирован: 17.05.2005 11:27:01

Re: Фоновое выполнение и синхронизация потоков

Сообщение iN0k » 27.06.2013 17:27:21

Ему и необязательно знать.
В раздел uses вставляем библ. Forms, и теперь у вас волшебным образом появляется доступ к переменной Application. :)
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Фоновое выполнение и синхронизация потоков

Сообщение svk12 » 27.06.2013 17:29:22

ssl писал(а):Я пока никак не могу понять, как мне ждать ИЛИ события от интерфейса, ИЛИ завершения своего задания.


Ждать ничего не надо.
После завершения задания дополнительный поток сам обновит данные в главном потоке.
Чтобы работало и под Линухом, надо в начале .lpr прописать нечто вроде:
Код: Выделить всё
{$IFDEF UNIX} {$DEFINE UseCThreads} {$ENDIF}


Допустим, имеем модуль:

Код: Выделить всё
unit SomeUnit;

inteface

TForm1=class(TForm)
  LocalData:TDataSet;
//Набор данных, связанный с визуальными элементами, в котором будут сохраняться результаты запроса.
//Это может быть некий MemoryDataSet, DBf, SQLite и т.д.
  procedure GetDataInThread;
//Метод, запускающий запрос.
end;


  TDataThread = class(TThread) //Сабдж!
  private
    { private declarations }
  protected
    { protected declarations }
    TheDatabase: TZConnection;
    TheQuery: TZReadOnlyQuery;
//Допустим, используется ZeosDBo.
  public
    { public declarations }
   MainForm:TForm1;
//Ссылка на главную форму.
    ParamsList:TStringList;
//Список настроек, необходимых  для выполнения запроса
    constructor Create;
    procedure Execute; override;
//Код, исполняемый в отдельном потоке.
    procedure Data2Main;
//Копирование даных в главный поток.
  end;                           

var
  DataThread:TDataThread=nil;

implementation

procedure TForm1.GetDataInThread;
begin
  if not Assigned(DataThread) then DataThread:=TDataThread.Create;
  DataThead.MainForm:=Self;
  With DataThread.ParamsList do
  begin
     //Установка параметров.
  end;
  DataThread.Start;
  //Процесс пошел - занимаемся другими делами.
end;

constructor  TDataThread.Create;
begin
  inherited Create(True);//CreateSuspended:=True;
  FreeOnTerminate := False; 
  TheDatabase:=TZConnection.Create(nil);
  TheQuery:=TZReadOnlyQuery.Create(nil);
  TheQuery.Connection:=TheDataBase;
  ParamsList:=TStringList.Create;       
end;

procedure TDataThread.Execute;
begin
  With ParamsList do
  begin
     //Настройка соединения.
  end; 
  TheDatabase.Connect;
  TheQuery.Open; 
  Synchronize(@Data2Main); 
  Stop;
//Наверное, необязательно - и так должен остановиться.
end;

procedure TDataThread.Data2Main;
begin
  With MainForm.LocalData do
  begin
     DisableControls;
    //Копируем данные.
     EnableControls;
  end;
end;

end.


Вот и всё!
svk12
постоялец
 
Сообщения: 409
Зарегистрирован: 09.06.2008 18:42:47

Re: Фоновое выполнение и синхронизация потоков

Сообщение ssl » 27.06.2013 18:39:10

Ему и необязательно знать.
В раздел uses вставляем библ. Forms, и теперь у вас волшебным образом появляется доступ к переменной Application


А если компонент будет использоваться в чисто консольном приложении? Или в демоне? Не смотрел ещё конкретно, но, думаю, бинарник распухнет не меньше, чем в дельфи. Причём, бесполезным кодом. Пока сделаю Event у компонента, а потом видно будет.

Вот и всё!

Если бы! :))

Смысл создавать поток, если он остановится на Synchronize, а Data2Main будет выполняться весь в главном потоке?
ssl
новенький
 
Сообщения: 59
Зарегистрирован: 17.05.2005 11:27:01

Re: Фоновое выполнение и синхронизация потоков

Сообщение svk12 » 27.06.2013 19:09:43

ssl писал(а):Смысл создавать поток, если он остановится на Synchronize, а Data2Main будет выполняться весь в главном потоке?


Ожидание отклика сервера после TheQuery.Open будет происходить в доп. потоке и не подвесит приложение.
При копировании данных в Data2Main можно сколько угодно вызывать Application.ProcessMessages, что тоже
разморозит GUI.
svk12
постоялец
 
Сообщения: 409
Зарегистрирован: 09.06.2008 18:42:47

Re: Фоновое выполнение и синхронизация потоков

Сообщение ssl » 27.06.2013 19:10:32

To iN0k:
:idea:
О. Кажись, работает. Только вместо ThreadSwitch поставил Sleep(1) - тогда процессор не жрёт 100% (Ubuntu).
Спасибки за идею!
ssl
новенький
 
Сообщения: 59
Зарегистрирован: 17.05.2005 11:27:01


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 212

Рейтинг@Mail.ru