Организация многопоточного приложения, со связкой форма-поток
Пишу многостраничное приложение для работы с СУБД Oracle.
Каждая страничка - это форма загруженная из bpl файла или созданная динамически из XML-какраса.
К исподникам bpl-формы доступа нету. Исполняемым "кодом" второй является скрипт.
В обоих случаях нету возможности "встроить" многопоточность непосредственно в файл формы.
Компоненты для работы с Oracle потокобезопасные.
Вопрос
[COLOR="Red"]Как лучше всего организовать многопоточность приложения, чтобы для каждой формы загруженной из файла или созданной динамически поставить в соответствие отдельный поток. Поток должен уничтожатся вместе с формой, после вызова Close() последней.[/COLOR]
[Добавлено 09.01.2008 в 03:21]
Вопрос был задан некорректно.
Переформулирую:
Как лучше всего организовать многопоточность приложения, чтобы для каждой формы загруженной из файла или созданной динамически создать отдельный поток, который будет полностью управлять (из которого пользователь будет управлять) этой формой и всеми дочерними [формами].
Предположительно, поток должен будет поддерживать свою очередь сообщений.
Гдавная программа знает только о существовании объекта типа TForm (по-факту, это будет наследник TForm) и экземпляра TThread (потомка). При вызове TThread::Terminate(), должен вызыватся TForm::Close() и наоборот.
И ВСЕ! Больше, никаких инструментов управления/воздействия на модули "главная форма программы"/программа иметь не должна.
Вся логика создания управляющего потока должна быть реализована в потомке TThread.
Конечно, это только примерный алгоритм и Вы можете предложить свои. Для меня только важно, чтобы "главная форма программы"/программа работала с формами как с TForm.
Пример, для чего все это нужно:
В программе p1, доступно 2 модуля m1 и m2. Оба модуля представлены главными формами, которые, при старте, распологаются на вкладках главной формы программы p1 (в общем случае, а в частности может быть и по-другому).
Пусть:
1. В модуле m1 происходят какие-либо долгие процессы. Например создание отчета.
2. Разработчики этого модуля, почему-то поленилесь вынести создание отчета (и/или других процессов) в отдельный поток.
Тогда, для того, чтобы пользователю воспользоватся модулем m2, надо открыть вторую конию (запустить еще раз) программы p1.
Ну, по-моему, уже и так понятно суть, но я все-таки поясню:
Для того чтобы, пользователю не надо было открывать вторую копию программы и необходимо вынести все действия модулей m1 и m2 в отдельные потоки (может быть, со своими очередями сообщений). При этом, мы не знаем что это будут за действия.
По суте, в программе p1 создаются 2 подпрограммы у которых главными окнами будут главные окна модулей m1 и m2.
Тоесть можно сделать какой либо менеджер окон, который будет создавать окна, связывать их в одном объекте с сущьностью отвечающей за нужную функциональность и выполняющуюся в отдельном потоке. Это объект опячть же и должен следить за своим временем жизни синхронно с окном "формы".
Если ты внимательно прочитаешь мой пост, то не найдешь там ничего противоречащего твоему утверждению.
Если бы ты внимательно прочитал мой первый пост (а я так понял что не внимательно), то ты бы понял, что я спрашиваю именно о том КАК связывать их в одном объекте с сущьностью отвечающей за нужную функциональность и выполняющуюся в отдельном потоке.
Может быть есть какие-либо наработки? Поиск по этому поводу молчит.
Если бы ты внимательно прочитал мой первый пост (а я так понял что не внимательно), то ты бы понял, что я спрашиваю именно о том КАК связывать их в одном объекте с сущьностью отвечающей за нужную функциональность и выполняющуюся в отдельном потоке.
Может быть есть какие-либо наработки? Поиск по этому поводу молчит.
Ну так сказали же тебе наработку - создать класс-менеджер, задачей которого будет создание форм, связывание их с потоком, отслеживание состояний пар объектов и очистка памяти по завершению.
Можно эту задачу возлагать на класс приложения - но ИМХО это не самое удачное решение. Можно называть это классом-менеджером, я называю классом-прокси - его задача управление объектами и передача сигналов.
Для более подробного ознакомления - паттерн проектирования "Фабрика классов".
Вопрос
Как лучше всего организовать многопоточность приложения, чтобы для каждой формы загруженной из файла или созданной динамически поставить в соответствие отдельный поток. Поток должен уничтожатся вместе с формой, после вызова Close() последней.
Лучше, чтобы весь GUI работал в одном потоке. Так и проще (не надо будет делать цикл обработки сообщений во всех потоках) и логичнее.
Каждая такая форма является "обозревателем" (см. соотв. паттерн проектирования) и регистрируется для получения соотв. сообщений (сигналов) в некотором менеджере. В менеджере существует потокобезопасная очередь в которую бизнес-логика, выполняющаяся в нескольких потоках, складывает сообщения. В таком варианте создание потоков и форм GUI независимо.
Можно обойтись без общего менеджера, если в нем нет особой необходимости (например, одной форме соответствует один поток), тогда при создании потока ему передается ссылка на обозреватель. Далее этот новый поток "дергает" соотв. методы формы по мере необходимости. В этом случае должен быть некоторый менеджер, который создает сначала форму, потом поток и, возможно, контролирует соответствие их времени жизни.
Можно обойтись без общего менеджера, если в нем нет особой необходимости (например, одной форме соответствует один поток), тогда при создании потока ему передается ссылка на обозреватель.<skiped>
Добавлю. Без общего менеджера стоит реализовывать если формы создаются модально (и разрушаются по месту создания) и отображают данные относительно независимые, в противном случае - лучше потратить время и реализовать нормально класс-менеджер, но это ИМХО. По крайней мере в своих задачах, я исхожу именно из этого.
Вам не кажется, что это именно этот случай?
Уважаемые Green и kot_, спасибо Вам за ответы. Но Вы просто ходите вокруг-да-около, извините за ассоциацию.
Я по-моему, вполне точно описал задачу, но может быть не очень... ?
...Без общего менеджера стоит реализовывать если формы создаются модально...
Формы создаются не модально, а наподобии страниц в Opera.
Я все-таки, думаю, что обработку сообщений должна взять на себя форма, а поток только посылать их [сообщения] ей.
Мне во-первых, не нужна независимость создания потоков и форм. Во-вторых, такой вариант мне кажется слишком сложным и надуманным. В том смысле, что VCL должен поддерживать и более лёгкий путь.
Можно обойтись без общего менеджера, если в нем нет особой необходимости (например, одной форме соответствует один поток), тогда при создании потока ему передается ссылка на обозреватель. Далее этот новый поток "дергает" соотв. методы формы по мере необходимости. В этом случае должен быть некоторый менеджер, который создает сначала форму, потом поток и, возможно, контролирует соответствие их времени жизни.
Вот именно то, что нужно <(например, одной форме соответствует один поток)> - именно так и ставилась задача.
Только я не пойму - зачем поток будет дергать какие-то методы, когда форма должна сама фыполнять свою бизнес-логику, а поток выступает только в качестве некоего "контейнера", благодаря которому форме доставляются сообщения от Windows если эта форма не выполняет сейчас никакого длительного процесса.
Время жизни потока и формы контролирует главный поток [приложения] или главная форма.
Рассуждать про патенты и как это можно сделать теоретически это конечно хорошо и даже очень поучительно, но хотелось бы увидеть реальный кусок кода, который сопоставил бы некое Win32 окно, например через Handle с instance некоего процесса.
Например:
AssiciateWindowToThread(newForm->Handle, new Thread);
Вам не кажется, что это именно этот случай?
Уважаемые Green и kot_, спасибо Вам за ответы. Но Вы просто ходите вокруг-да-около, извините за ассоциацию.
Я по-моему, вполне точно описал задачу, но может быть не очень... ?
Нет, не кажется. IMHO это тот случай, когда конкретный ответ просто не может быть воспринят не знаю уж в силу чего. Ответы тоже были весьма конкретными.
Я все-таки, думаю, что обработку сообщений должна взять на себя форма, а поток только посылать их [сообщения] ей.
Что ты подразумеваешь под сообщениями? Windows Message? Конечно, оконные сообщения должно обрабатывать окно. :)
Формы создаются не модально, а наподобии страниц в Opera.
Мне во-первых, не нужна независимость создания потоков и форм. Во-вторых, такой вариант мне кажется слишком сложным и надуманным. В том смысле, что VCL должен поддерживать и более лёгкий путь.
Вариант этот - обычная практика для многооконных интерфейсов. В FireFox сделанно именно так. Уверен, что и в Opera (раз уж ты о ней упомянул), и др. броузерах так же.
При чем тут VCL, вообще, не понятно.
Вот именно то, что нужно <(например, одной форме соответствует один поток)> - именно так и ставилась задача.
Только я не пойму - зачем поток будет дергать какие-то методы, когда форма должна сама фыполнять свою бизнес-логику,
Потому, что окно(форма) это лишь способ представления данных. У тебя же вычислительными задачами занимается компьютер, а не монитор.
Разделять бизнес-логику и UI - это стандартная архитектура MVC:
http://ru.wikipedia.org/wiki/Model-view-controller
а поток выступает только в качестве некоего "контейнера", благодаря которому форме доставляются сообщения от Windows если эта форма не выполняет сейчас никакого длительного процесса.
Чего=то я совсем тебя не понимаю... форма у тебя сама по себе живет что ли? Вне потока? :)
Что значит "форма не выполняет длительного процесса"? :)
Рассуждать про патенты и как это можно сделать теоретически это конечно хорошо и даже очень поучительно,
Мы не рассуждали про паттерны, а дали тебе конкретные ответы. Паттерны для того и созданы, чтоб формализовать описание решения.
но хотелось бы увидеть реальный кусок кода, который сопоставил бы некое Win32 окно, например через Handle с instance некоего процесса.
Так и надо было задавать вопрос: "напишите мне код", а не "Как лучше всего организовать".
Например:
AssiciateWindowToThread(newForm->Handle, new Thread);
Так не получится. Сообщения будут приходить тому потоку, в котором окно(форма) было создано. Я тебе об этом уже говорил в предыдущем посте.
Ещё раз говорю: создавай окна в одном потоке, а бизнес-логику выполняй в других потоках. Отработал поток свои вычисления, стопори его или уничтожай (первое предпочтительнее) до новой необходимости. Все же элементарно.
Ещё раз говорю: создавай окна в одном потоке, а бизнес-логику выполняй в других потоках. Отработал поток свои вычисления, стопори его или уничтожай (первое предпочтительнее) до новой необходимости. Все же элементарно.
...
Ты не правильно видешь поставленную задачу. Главный поток программы не дожен ничего знать о вторичных потоках (кроме того, что они существуют), а тем более о формах которые абсолютно независымы в смысле бизнес-логики.
Ещё раз говорю: создавай окна в одном потоке, а бизнес-логику выполняй в других потоках. Отработал поток свои вычисления, стопори его или уничтожай (первое предпочтительнее) до новой необходимости. Все же элементарно.
Интересно, как извлечь вычисления из скомпилированной формы, которая извлекается из BPL методом Install() ???
В общем, на основании всей предыдущей дискуссии я думаю надо сделать так:
1. Создать потомка от TThread
2. Научить потомка создавать формы по TMetaClass'у
3. Создавать экземпляр потомка и передавать в качестве параметра в конструктор TMetaClass
Вроде так должно работать...
Интересно, как извлечь вычисления из скомпилированной формы, которая извлекается из BPL методом Install() ???
Какая разница каким образом форма создается? И что значит - "как извлечь вычисления"? Форма, как сказал Green, это всего лишь способ представления данных для пользователя. Если в классе формы проводятся какие либо вычисления - реализуй методы доступа к их результатам. В чем затруднение - для меня лично непонятно.
В общем, на основании всей предыдущей дискуссии я думаю надо сделать так:
1. Создать потомка от TThread
2. Научить потомка создавать формы по TMetaClass'у
3. Создавать экземпляр потомка и передавать в качестве параметра в конструктор TMetaClass
Вроде так должно работать...
вероятно работать будет. Хотя по поводу создания форм и потоков выше уже говорилось - повторяться не буду. Я бы для работы с потоком не использовал бы TThread - уж слишком он малофункционален и громоздок. Проще создать свой класс и реализовать в нем необходимый функционал. Зачем в потоке создавать форму - тебе виднее. ИМХО, и я согласен с вышесказанным, это ошибка проектирования.
//при создании формы
void __fastcall TForm1::FormCreate(TObject *Sender)
{
DWORD p1;
hT1 = CreateThread(NULL,0,Thread1,NULL,0,&p1); //запуск потока
}
//при закрытии
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
TerminateThread(hT1); //прибить
}
//при создании формы
void __fastcall TForm1::FormCreate(TObject *Sender)
{
DWORD p1;
hT1 = CreateThread(NULL,0,Thread1,NULL,0,&p1); //запуск потока
}
//при закрытии
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
TerminateThread(hT1); //прибить
}
тут другая тема: заранее ничего не известно про создание формы, она берется из dfm файла, все отдано на откуп юзера
//при создании формы
void __fastcall TForm1::FormCreate(TObject *Sender)
{
DWORD p1;
hT1 = CreateThread(NULL,0,Thread1,NULL,0,&p1); //запуск потока
}
//при закрытии
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
TerminateThread(hT1); //прибить
}
Здорово, только вот никакого отношения к теме не имеет, т.к. и создаваемый поток ничего не знает о форме, и форма "работает" (обрабатывает оконные сообщения) в другом потоке.
Да и с архитектурной точки зрения это ошибка, чтоб элемент UI занимался порождением потоков.
Как лучше всего организовать многопоточность приложения, чтобы для каждой формы загруженной из файла или СОЗДАННОЙ ДИНАМИЧЕСКИ поставить в соответствие отдельный поток. Поток должен уничтожатся вместе с формой, после вызова Close() последней.
Каков вопрос, таков ответ.
Ну и пусть посылает сообщения компонентам формы.
Все это уже предлагалось.
Так вот, в том-то и дело, что я не знаю, что будет происходить в форме - будь то вычесления или просто показ пользователю слайдов. Да меня это и не касается.
Поэтому и не могу реализовать методы доступа т.к. см. выше .
Все это уже предлагалось.
Ну Green кажется понял, что я хочу.
В общем, на основании всей предыдущей дискуссии я думаю надо сделать так:
1. Создать потомка от TThread
2. Научить потомка создавать формы по TMetaClass'у
3. Создавать экземпляр потомка и передавать в качестве параметра в конструктор TMetaClass
Вроде так должно работать...
Так, к сожалению, не работает :(
Пример, для чего все это нужно:
В программе p1, доступно 2 модуля m1 и m2. Оба модуля представлены главными формами, которые, при старте, распологаются на вкладках главной формы программы p1 (в общем случае, а в частности может быть и по-другому).
Пусть:
1. В модуле m1 происходят какие-либо долгие процессы. Например создание отчета.
2. Разработчики этого модуля, почему-то поленилесь вынести создание отчета (и/или других процессов) в отдельный поток.
Тогда, для того, чтобы пользователю воспользоватся модулем m2, надо открыть вторую конию (запустить еще раз) программы p1.
Ну, по-моему, уже и так понятно суть, но я все-таки поясню:
Для того чтобы, пользователю не надо было открывать вторую копию программы и необходимо вынести все действия модулей m1 и m2 в отдельные потоки (может быть, со своими очередями сообщений). При этом, мы не знаем что это будут за действия.
По суте, в программе p1 создаются 2 подпрограммы у которых главными окнами будут главные окна модулей m1 и m2.
P.S. Под очередями сообщений, я конечно же имею введу[всегда] очереди сообщений Windows. А то, где-то вверху непонятка вышла...
P.P.S. Я переформулировал/уточнил/разжевал вопрос. См. первый пост.