И опять про потоки и про формы

Basketbol
Дата: 21.07.2019 18:37:14
Добрый день!
Прочитал много всего про потоки, в том числе и на этом форуме.
Но не могу понять как правильно реализовать мою задачу.
Максимально упрощенная задача:
Нужно выполнять параллельно несколько потоков, которые меняют свойство progress компонента-бегунка TGaude.
Как я пытаюсь сделать:
Есть форма с TGaude на нем.
Есть поток:
type
  TThreadMyForm = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  public
    FMyForm : TMyForm; //Ссылка на форму
  end;


Я создаю поток suspended = true, после создания присваиваю его публичному свойству FMyForm ссылку на только что созданную форму, и запускаю на выполнение:

    myForm := TMyForm.Create(Application);

    threadMyForm := TThreadMyForm.create(true);
    with threadMyForm do
    begin
      freeOnTerminate := true;
      FWebForm := webForm;
      FMyForm := myForm;
      Resume;
    end;


В самой форме описана процедура изменения ползунка. И этот метод я вызываю в цикле Execute потока.

procedure TMyForm.actionChangeGaude(milisek : integer);
var
  i: Integer;
begin
  gaude.Progress := 0;
  gaude.MaxValue := milisek;

  for i := 0 to milisek do
  begin
    sleep(100);
    gaMain.Progress := i;
    Application.ProcessMessages;
  end;
end;


Этот метод не знает что он вызван в Execute потока.


Я не понимаю как нужно сделать возможность безопасного закрытия формы пользователем в момент выполнения потока.

Когда я просто закрываю форму - поток остается работать, и пытается что то менять на уже уничтоженной форме. (Форма уничтожается в момент закрытия).

Т.е. конечно же нужно перед закрытием формы уничтожить поток, который на этой форме что то делает.
Следовательно - форма должна знать о потоке.
Следовательно - после создания потока я указываю его как значение поля формы FMyThread:
   myForm.FThread := myThread as TThread;


И в onclose формы делать так:

    FThread.Terminate;
    FThread.WaitFor;
    FThread.Free;


Но эксперименты показывают что это все неправильно, ибо вылазит ошибка на WaitFor : "Неверный дескриптор"

Ну и то что поток вызывает метод формы, который описан в самой форме, и который не знает что его вызвал поток - это кмк тоже неправильно.

Какая должна быть правильная архитектура такой задачи?
Basketbol
Дата: 21.07.2019 18:45:45
Может генерировать события?
Пользователь вызывает событие closeQuery у формы.
В closeQuery мы пишем потоку terminate.
И после создания потока мы подписываемся на событие onTerminate потока.
И после возникновения события устанавливаем переменную FCanClose в true
Как то так что ли...
...
Короче - каша в голове.
Не хватает опыта работы с потоками...
Basketbol
Дата: 21.07.2019 20:10:41
Нашел ошибку: после уничтожения потока нужно полю формы FThread (то которое ссылается на управляющий поток) присваивать nil.
...
Но все равно - архитектура какая то люто неправильная...
Все на все ссылается....
Никакой абстракции...
..
Basketbol
Дата: 21.07.2019 20:28:41
Пришел к тому, что действия которые что то делают с формой, должны быть описаны в классе потока.
Также как и какие то длительные действия - паузы, ожидания чего-либо.
Ибо в классе самого потока я смогу анализировать поле terminated экземпляра потока, и вовремя прекращать.

Т.е. создаем форму.
Создаем поток.
Полю FMyForm потока присваиваем myForm
И наоборот: полю FMyThread формы присваиваем ссылку на поток.

Когда пользователь закрывает форму руками, мы сначала уничтожаем поток, и ждем его завершения.
Чтобы это было максимально быстро - во всех действиях ожиданиях и паузах, описанных в потоке анализируем terminated.

Ну и после уничтожения потока полю формы FmyThread присваиваем nil.

Не красиво, но работать будет.
alekcvp
Дата: 21.07.2019 21:04:09
Basketbol
Прочитал много всего про потоки, в том числе и на этом форуме.

Вы, судя по всему, даже комментарий, который среда создаёт для нового потока, не прочитали.
{
  Important: Methods and properties of objects in visual components can only be
  used in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure ввв.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end;

    or

    Synchronize(
      procedure
      begin
        Form1.Caption := 'Updated in thread via an anonymous method'
      end
      )
    );

  where an anonymous method is passed.
alekcvp
Дата: 21.07.2019 21:06:29
Basketbol,

Не говоря уже о том, что вы вообще не понимаете что вы делаете:
  freeOnTerminate := true;
  ....
  FThread.Terminate;
  FThread.WaitFor;  <-- К этому моменту уже может быть вызван FThread.Free (см. выше), что вы ждёте от этого вызова?.. 
  FThread.Free;
makhaon
Дата: 21.07.2019 21:19:29
может применить компоненту, которая инкапсулирует всю работу с потоком внутри?
их есть несколько разных.
то, что сейчас сделано, конечно, можно в сотый раз поругать, но не знаю надо ли )
YuRock
Дата: 21.07.2019 21:20:39
alekcvp
Basketbol,

Не говоря уже о том, что вы вообще не понимаете что вы делаете:
  freeOnTerminate := true;
  ....
  FThread.Terminate;
  FThread.WaitFor;  <-- К этому моменту уже может быть вызван FThread.Free (см. выше), что вы ждёте от этого вызова?.. 
  FThread.Free;
Не говоря о том, что Terminate и WaitFor будут вызваны в деструкторе (если он вызовется внутри Free)
X-Cite
Дата: 21.07.2019 22:45:28
Basketbol
Но не могу понять как правильно реализовать мою задачу.
Нужно выполнять параллельно несколько потоков, которые меняют свойство progress компонента-бегунка TGaude.


Какая задача на самом деле? Вы сейчас описали не задачу, а реализацию...
Basketbol
Дата: 22.07.2019 00:38:44
YuRock
alekcvp
Basketbol,

Не говоря уже о том, что вы вообще не понимаете что вы делаете:
  freeOnTerminate := true;
  ....
  FThread.Terminate;
  FThread.WaitFor;  <-- К этому моменту уже может быть вызван FThread.Free (см. выше), что вы ждёте от этого вызова?.. 
  FThread.Free;
Не говоря о том, что Terminate и WaitFor будут вызваны в деструкторе (если он вызовется внутри Free)


Что waitFor противоречит freeOnTerminate = true - я прекрасно знаю. И что данный форум не позволяет редактировать посты - тоже. FreeOnTerminate я делаю false при создании, поэтому все норм с этим.
И что менять состояние vcl компонентов синхронайзом - я тоже знаю и делаю.
Вопрос был именно про архитектуру.