Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 2 109

/ вопрос открыт /

Доброго времени суток, уважаемые эксперты!
Вопрос такой:
Есть шаблон окна и потока (TThread). Во время выполнения приложения динамически по шаблону создаются парой и при выполнении взаимодействуют. Сколько таких пар будет создано зависит только от пользователя и их количество заранее не известно. В каждый объект (форма и поток) добавлено паблик свойство Index для того, чтобы при выполнении знать с каким объектом взаимодействовать. Попытался создать динамические массивы форм и потоков. При запуске приложение создает по шаблону форму и поток. При создании второй пары поток работает как нужно, а форма не создается, только меняется ее индекс и первый поток остается без управления. Как быть? Может есть какоенибудь другое решение?

Nasgool Вопрос ожидает решения (принимаются ответы, доступен мини-форум)

Вопрос задал: Nasgool (статус: 2-ой класс)
Вопрос отправлен: 24 ноября 2008, 06:20
Состояние вопроса: открыт, ответов: 3.

Ответ #1. Отвечает эксперт: min@y™

Чтобы не путаться с индексами, нужно просто в каждом объекте из пары создать ссылку на парный объект:

interface
 
type
  TMyThread = class(TThread);  // Заглушка 
 
  TMyForm = class(TForm)
  private
  protected
  public
    MyThread: TMyThread; // Ссылка на поток
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;
 
  TMyThread = class(TThread)
  private
    FForm: TMyForm;
  public
    property Form: TMyForm read FForm write FForm; // Ссылка на форму
  end;
 
implementation
 
constructor TMyForm.Create(AOwner: TComponent);
begin
  inherited;
  Self.MyThread:= TMyThread.Create(True);
end;
 
destructor TMyForm.Destroy;
begin
  FreeAndNil(Self.MyThread);
  inherited;
end;
 
end.

Синхронайзить потоки тоже следует методом формы-пары, я так думаю.

Ответ отправил: min@y™ (статус: Доктор наук)
Время отправки: 24 ноября 2008, 08:28

Ответ #2. Отвечает эксперт: ANBsoft

Здравствуйте, Nasgool!
Попробуйте так:
var
Form1: TForm1;
MyForm:Array [1..10] of ^TForm2;//Максимальное количество возможных открытых форм
k:Integer;

procedure TForm1.Button1Click(Sender: TObject);
Var F2:TForm2;
begin
Inc(k);
Application.CreateForm(TForm2, F2);
MyForm[k]:=@F2;
MyForm[k].Name:='MyForm'+IntToStr(k);
MyForm[k].Show;
end;

Ответ отправил: ANBsoft (статус: Студент)
Время отправки: 24 ноября 2008, 09:01

Ответ #3. Отвечает эксперт: Вадим К

Здравствуйте, Nasgool!
Почитал ответы экспертов, ужаснулся. Если ещё min@y™ пишет вменяемый ответ, то Бубырь Александр Николаевич - мне вообще не понятно.
Поэтому пишу свое решение. Первым долгом надо решить, кто важнее - потоки либо формы. Я так подозреваю, что формы - вторичное (пишеться что то DownLoadManager'a?)
Поэтому, лучше всего делать так. Заводим массив потоков, его оптимальнее организовать с помощью наследника от класса TList. Потоки сами должны себя оттудова удалять и добавлять. О формах потоки не знают ничего, кроме некого ида, который есть просто числом, которое увеличивается с каждым новым потоком. Таким образом, они будут уникальны. А также поток должен знать о командах, понимаемыми формами. Сами команды организовываются на основании PostMessage/SendMessage. Ещё раз уточню - поток может отсылать данные в главный поток и при этом он не знает, ни как выглядит форма, ни как она отображает данные и отображает ли вообще.
Теперь, как оргранизовать формы. Для этого, у главной формы есть метод, который умеет искать нужную форму, открыть при надобности и передать данные.
Как сделать подобное групповое действие - рассказано в моей статье О формах.

Зачем так сложно? дело в том, что только разделив код на кусочки, можно заставить всё это работать в максимально быстрые сроки.
Кода не привожу, так как не знаю, что с этого понятно, а что нет. Пишите в минифорум, будем по кусочкам разбирать.

Ответ отправил: Вадим К (статус: Академик)
Время отправки: 24 ноября 2008, 11:58
Оценка за ответ: 5

Комментарий к оценке: С вами приятно общяться иметь дело.

Мини-форум вопроса

Всего сообщений: 20; последнее сообщение — 24 ноября 2008, 23:58; участников в обсуждении: 5.
min@y™

min@y™ (статус: Доктор наук), 24 ноября 2008, 08:30 [#1]:

Чорт, забыл:
constructor TMyForm.Create(AOwner: TComponent);
begin
  inherited;
  Self.MyThread:= TMyThread.Create(True);
  Self.MyThread.Form:= Self;                     // Вот так
end;
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
ANBsoft

ANBsoft (статус: Студент), 24 ноября 2008, 12:33 [#2]:

Насколько я понял, у автора была проблема с созданием массива идентичных форм, а не с открытием потоков. Судя по всему у него всегда отображался один и тот же экземпляр формы.
В статье Вадима об этом ничего не сказано явно.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 12:34 [#3]:

Сам поработал над этим немного и с мертвой точки сдвинулся.
Потоки и формы создаются как нужно. Проблема только в том как управлять потоком. Впринципе он работает автономно. Но его пользователю нужно както приостановить\запустить или удалить. Пока с этим проблема. Почитаю статью - напишу что получилось.
Вадим К

Вадим К (статус: Академик), 24 ноября 2008, 12:52 [#4]:

Приостановить, а тем более прибить поток надо делать только с самого потока. То есть, основной, управляющий поток посылает комманду "стоп" в виде установки специальной переменной. А поток время от времени читает. Если видит, что надо остановиться, то просто выходит с процедуры Execute. Если надо на паузу поставиться, выполняет suspend. Запустить поток можно только с другого потока (как остановленный поток может себя запустить?:) ).Это делается с помощью имяпотока.resume;
Саму эту переменную синхронизировать не надо, так как она читаться будет только с одного потока(можно и с многих), а писаться только с другого (важно, что бы только один поток мог модифицировать, у нас это главный будет).
Почему потоки нельзя прибить с главного потока? А по одной простой причине - мы не знаем, в каком он находиться состоянии. И хотя функция для убиения потоков есть, но вииндовс не гарантирует, что поток будет корректно удален и ресурсы почищены.
Кстати, вызов имяпотока.Terminate; просто выставляет переменную Terminated в True;
Галочка "подтверждения прочтения" - вселенское зло.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 13:32 [#5]:

С формами все ОК:

Шаблон:

type
TForm3 = class(TForm)
...
private
public
{ Public declarations }
Indx: Integer;
end;

В главной форме:

var
...
Forms: array of TForm3;
...
procedure TForm1.Button1Click(Sender: TObject);
var
I:Integer;
begin
I := Length(Forms);
SetLength(Forms, I + 1);
Application.CreateForm(TForm3, Forms[I]);
Forms[I].Indx := I;
Forms[I].Show;
Forms[I].SetFocus;
end;

С потоками такая же тема не проходит. При обращении к любому из методов потока, кроме нулевого (созданного первым) происходит исключение "Неверный дескриптор (6)". В чем проблема?
Вадим К

Вадим К (статус: Академик), 24 ноября 2008, 13:55 [#6]:

>>Forms: array of TForm3;
Ой не делал бы я так. А когда форму удалит с массива надо будет, ой проблем будет....
А потоки создаются как?
Галочка "подтверждения прочтения" - вселенское зло.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 14:13 [#7]:

Форма удаляется так:
ZeroMemory(Forms[Indx], SizeOf(Forms[Indx]));
При создании новой формы Indx увеличивается и все.
Потоки попытался создавать как же. Результат выше изложен.
При том решить что важнее, формы или потоки немогу. Потоки, синхронизируясь, меняют визуальные свойства формы (как созданной в пару, как и главной) во время выполнения и управляются как с главной формы, так и с парных им.
Вадим К

Вадим К (статус: Академик), 24 ноября 2008, 14:18 [#8]:

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

После всех предварительных настроек потока, его запускаем?
типа
Self.MyThread.Resume;?
Галочка "подтверждения прочтения" - вселенское зло.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 14:28 [#9]:

в шаблоне создаваемой формы:
implementation

uses
Unit1;

...

procedure TForm3.Button1Click(Sender: TObject);
begin
Form1.FormDestroy(Indx);
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
Threads[Indx].Suspend;
end;

procedure TForm3.Button3Click(Sender: TObject);
begin
Threads[Indx].Resume;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
SetLength(Threads, Indx + 1);
Threads[Indx] := QWE.Create(False);
Threads[Indx].Indx := Indx;
end;

из основного проекта не стал дергать код. слишком много лишнего.
создал проекТИК специально для решения проблемы и проработки алгоритма :)
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 14:29 [#10]:

Вот код потока:

unit Unit2;

interface

uses
Classes, SysUtils;

type
QWE = class(TThread)
private
{ Private declarations }
protected
P1,P2,P3,P4:Integer;
procedure Execute; override;
procedure Vis;
public
Indx: Integer;
end;

implementation

uses
Unit1;

procedure QWE.Execute;
begin
{ Place thread code here }
while True do
begin
P1 := Random(1000);
P2 := Random(1000);
P3 := Random(1000);
P4 := Random(1000);
Vis;
end;
end;

procedure QWE.Vis;
begin
Form1.StringGrid1.Cells[1,Indx + 1] := IntToStr(P1);
Form1.StringGrid1.Cells[2,Indx + 1] := IntToStr(P2);
Form1.StringGrid1.Cells[3,Indx + 1] := IntToStr(P3);
Form1.StringGrid1.Cells[4,Indx + 1] := IntToStr(P4);
Forms[Indx].Label1.Caption := IntToStr(P1);
Forms[Indx].Label2.Caption := IntToStr(P2);
Forms[Indx].Label3.Caption := IntToStr(P3);
Forms[Indx].Label4.Caption := IntToStr(P4);
Sleep(1000);
end;

end.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 14:38 [#11]:

Сейчас попробую как Вы, Вадим, предлагаете. Все действия с потоком, к которым обращаюсь извне (пауза, пуск, убить), заключить в сам поток и управлять ими через массив переменных с идентичными индексами.
Вадим К

Вадим К (статус: Академик), 24 ноября 2008, 14:38 [#12]:

О.... да тут глюков видимо не видимо. Во первых, нельзя обращаться к оконным компонентам с потока напрямую. НЕЛЬЗЯ! Надо делать синхронизацию.
Галочка "подтверждения прочтения" - вселенское зло.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 14:49 [#13]:

Спасибо. Ткнул носом. Сам знаю, но.... Ну как это бывает :)

в Execute потока не
Vis;
а
Synchronize(Vis);

Только синхронизировал и при старте потока приложение виснет.
Вадим К

Вадим К (статус: Академик), 24 ноября 2008, 14:58 [#14]:

Синхронизация выполнятся очень хитро при использовании метода Synchronize - она выполняется в главном потоке - иначе никак. Из этого следует, что одновременно две синхронизации не получиться....
Галочка "подтверждения прочтения" - вселенское зло.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 15:06 [#15]:

Что-то не допонял я.
Код потока:
procedure QWE.Execute;
begin
{ Place thread code here }
while True do
begin
P1 := Random(1000);
P2 := Random(1000);
P3 := Random(1000);
P4 := Random(1000);
Synchronize(Vis);
Sleep(1000);
end;
end;

procedure QWE.Vis;
begin
Form1.StringGrid1.Cells[1,Indx + 1] := IntToStr(P1);
Form1.StringGrid1.Cells[2,Indx + 1] := IntToStr(P2);
Form1.StringGrid1.Cells[3,Indx + 1] := IntToStr(P3);
Form1.StringGrid1.Cells[4,Indx + 1] := IntToStr(P4);
Forms[Indx].Label1.Caption := IntToStr(P1);
Forms[Indx].Label2.Caption := IntToStr(P2);
Forms[Indx].Label3.Caption := IntToStr(P3);
Forms[Indx].Label4.Caption := IntToStr(P4);
end;
Так по моему должно быть ?

Подредактировал: слип перенес из процедуры в основу потока. Из за етого создавалось впечатление висяка. Впринципе все стоит на том же месте. Второй созданный поток не запускается (т.е. Thread[Indx].Resume вызывает ошибку).
Вадим К

Вадим К (статус: Академик), 24 ноября 2008, 15:14 [#16]:

Sleep(1000); гадо убрать с функции синхронизации.
Галочка "подтверждения прочтения" - вселенское зло.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 15:20 [#17]:

Сам увидел. Уже исправил.
Предсообщение:
Подредактировал: слип перенес из процедуры в основу потока. Из за етого создавалось впечатление висяка. Впринципе все стоит на том же месте. Второй созданный поток не запускается (т.е. Thread[Indx].Resume вызывает ошибку).
Вадим К

Вадим К (статус: Академик), 24 ноября 2008, 15:32 [#18]:

Скорее всего поток умирает по какой то причине.
Надобно заключить тело процедуры execute в try-except и выводить сообщение о ошибке....
Галочка "подтверждения прочтения" - вселенское зло.
Nasgool

Nasgool (статус: 2-ой класс), 24 ноября 2008, 15:46 [#19]:

Все! Ура! Вопрос решен!
Создавалась форма, после ее свойству Indx присваивалось значение.
Форма на onCreate создавала поток и присваивала его Indx свой Indx.
Нужно не на onCreat (в этот момент Indx у формы не определена и равна 0), а на onShow с проверкой флага, например Tag созданой формы.

procedure TForm3.FormShow(Sender: TObject);
begin
if Tag = 0
then
begin
SetLength(Threads, Indx + 1);
Threads[Indx] := QWE.Create(True);
Threads[Indx].Indx := Indx;
Tag := 1;
end;
end;

Всем огромное спасибо, особенно Вам, Вадим. :)
DNK

DNK (статус: Студент), 24 ноября 2008, 23:58 [#20]:

Вопросу ставлю минус. Потому как спрашивалось почему код работает некорректно, а сам код не приведен. Конечно из минифоума все становится более или менее ясно, но вопросы и ответы в дальнейшем пойдут в рассылку, а минифорум увы нет.

По вопросу хочу добавить. Массив ссылок на формы программы не считаю вообще нужным заводить, т.к. у объекта Application уже есть свойство Forms содержащее все формы программы. В вашем случае достаточно с помощью оператора IS проверять соответствие класса формы.
"Digital Networked Knight"

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 12:02
Выполнено за 0.04 сек.