Организация приложений с несколькими потоками
Как известно [Cb], в общем случае процесс (выполняемая программа) может включать в себя не один, а несколько последовательностей команд (потоков или так называемых нитей — tread), на которые операционная система выделяет определенные кванты времени. Поэтому потоки выполняются как бы параллельно. Если в приложении имеются независимые друг от друга задачи (хотя бы частично зависимые), выполнение которых требует достаточно длительного времени, целесообразно для каждой из этих задач выделить свой поток или нить.
Например, одна нить выполнения может осуществлять основную работу, а вторая, с меньшим приоритетом, может в то же время готовить или реорганизовывать какие-то файлы, рисовать изображения, которые потребуются в дальнейшем, т.е. выполнять черновую работу. Другой пример — параллельная работа с несколькими внешними источниками информации.
Особенно большой выигрыш в производительности за счет параллельного выполнения нескольких нитей можно получить в многопроцессорных системах, в которых можно организовать выполнение каждой нити отдельным процессором.
Параллельно выполняемые нити работают в адресном пространстве одного процесса и могут иметь доступ к глобальным переменным этого процесса. Одним из способов создания приложения с несколькими потоками является использования компонент типа TThread. Этот компонент отсутствует в палитре библиотеки. TThread — это абстрактный класс, позволяющий создать в приложение, отдельную нить выполнения. Для того чтобы ввести TThread в свое приложение надо ваполнить команду «File | New Other» и в открывшемся окне Депозитария на странице New выбрать пиктограмму Thread Object. Вам будет задан вопрос об имени (Class Name) создаваемого класса, наследующего TThread. Укажите любое имя (например Т) и в ваш проект добавится новый модуль, файлы которого имеют вид:
Заголовочный файл Unit2.H:
Созданный C++Builder модуль, как вы можете видеть, содержит заготовку классов с введенным вами именем (в нашем пример — Т), наследующего TThread. Вы можете добавлять в него любые свойства и методы, учитывая отмеченные в комментариях области видимости. Процедура Execute, заготовку которой вы можите видеть в коде, является основной процедурой нити. При ее окончании завершается и выполнение данной нити приложения.
Класс наследует от TThread ряд методов. Прежде всего, это конструктор, создающий объект нити:
_fastcall TThread(bool CreateSuspended);
Параметр CreateSuspended конструктора определяет способ выполнения нити. Если CreateSuspended = false, то выполнение процедуры Execute начинается немедленно после создания объекта. Если CreateSuspended = true, то выполнение начинается только после того, как будет вызван метод Resume:
vojd _fastcall Resume (void);
Конструктор TThread не должен вызываться в приложении явным образом.
Для создания объекта класса TThread, как и для всех классов VCL, надо использовать операцию new. Например:
T *SecondProcess = new Т(true); SecondProcess->Resume();
Функция Resume имеет две области применения. Во-первых, она запускает выполнение, если объект нити был создан с CreateSuspended = true. Во-вторых, она запускает приложение, приостановленное ранее методом Suspend:
void _fastcall Suspend(void);
Таким образом, вы можете в любой момент приостановить выполнение нити методом Suspend, а затем продолжить выполнение методом Resume. Если вы подряд несколько раз вызвали метод Suspend, то выполнение возобновится только после того, как столько же раз будет вызван метод Resume. Узнать, является ли нить приостановленной, можно по значению булева свойства Sus-pended.
В функции Execute можно непосредственно писать операторы выполнения, вызовы каких-то иных функций и т.п. Однако если функция должна вызывать какие-то методы компонентов VCL или обращаться к свойствам формы, то необходимо соблюдать осторожность, поскольку при этом не исключены конфликты между параллельно выполняемыми нитями. В этом случае в функции надо вызывать метод Synchronize, как сказано в комментарии, который вы могли видеть в приведенной выше заготовке модуля. Метод определен следующим образом:
Typedef void _fastcall (_closure *TThreadMethod) (void); void _fastcall Synchronize(TThreadMethod &Method);
В этом определении Method — функция, работающая с компонентами VCL. Таким образом, при работе с компонентами VCL надежнее строить выполнение следующим образом. Вы пишете функцию, выполняющую необходимые действия с компонентами VCL.
Пусть вы дали ей имя Work. Тогда вы включаете ее объявление в класс нити, например, в раздел private, даете в файле реализации ее описание, а процедура Execute в этом случае может, например, состоять из единственного оператора Synchronize(Work):
заголовочный файл Unit 2.h:
файл реализации Unit2.cpp:
Нормальное завершение выполнения нити происходит при завершении процедуры Execute. Однако возможно и досрочное завершение выполнения нити. Для этого в ее процедуру Execute должна быть введена проверка булева свойства Terminated (завершено). Нормально это свойство равно false. Но если какая-то внешняя нить вызвала метод Terminate объекта данной нити, то Terminated становится равным true. Если предполагается возможность такого завершения выполнения нити, то процедура Execute должна периодически проверять значение Terminate и при получении значения true должна завершаться. Например:
void _fastcall T::Execute() < do < . >while (!Terminated); >
Метод Terminate обеспечивает «мягкое» завершение нити. Процедура Execute сама решает, в какой момент ей удобно завершить выполнение. Имеется и более грубая функция API Windows — TerminateThread, вызывающая немедленное завершение выполнения нити. Например, оператор
TerminateThread ((void *) SecondProcess->Handle,0);
прервет выполнение объекта нити с именем SecondProcess. В этом операторе использовано свойство Handle (дескриптор) нити, позволяющее обращаться к функциям API Windows. Второй параметр функции TerminateThread (в приведенном примере 0) задает код завершения нити. Этот код можно прочитать в свойстве ReturnValue объекта нити.
Что именно происходит при завершении выполнения нити, определяется свойством FreeOnTerminate объекта типа TThread:
_property bool FreeOnTerminate,
Так, если FreeOnTerminate = true, то при завершении выполнения объект TThread разрушается, освобождая память. При FreeOnTerminate = false освобождать память надо явным применением к объекту операции delete.
Если одновременно выполняется несколько нитей, то может потребовать чтобы какая-то нить ожидала завершения другой нити, которая, например, готовит ей исходные данные. Такое ожидание можно осуществить, применив к нити, завершения которой надо ждать, метод WaitFor:
int _fastcall WaitFor(void);
Этот метод возвращает свойство ReturnValue — код завершения ожидаемой нити. Метод не возвращается, пока нить, которую ожидают, не завершит выполнения. Каждая выполняемая нить имеет определенный уровень приоритета, определяемый значением ее свойства Priority. Это свойство может иметь следующие значения:
Нить выполняется только когда система свободна.
Приоритет нити на два пункта ниже нормального.
Unit2 h и unit2 cpp
При открытом проекте (допустим, в нём есть модуль Unit1 и форма Form1 ) выполнить File->New->Form
Появился модуль Unit2 и форма Form2
В первом модуле ставим
#include «Unit2.h»
Или клавишами Alt+F11 (меню Files->Include Unit Hdr. )
После этого можем обращаться из первого модуля к данным второго.
Например, в Unit2.h написано:
#include #include #include //--------------------------------------------------------------------------- class TForm2 : public TForm < __published: // IDE-managed Components private: // User declarations public: // User declarations int iii=1; //Описали здесь свою переменную __fastcall TForm2(TComponent* Owner); >; //--------------------------------------------------------------------------- extern PACKAGE TForm2 *Form2; //--------------------------------------------------------------------------- #endif
Значит, Unit1.cpp (на его форме 1 кнопка Button1 и в коде 1 метод TForm1::Button1Click ) может быть таким:
//--------------------------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //Подключили второй модуль! //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) < >//--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) < String s; s=IntToStr(Form2->iii); //Использовали переменную из класса второй формы ShowMessage (s); >
Если мы подключили модуль без формы (меню File->New->Unit ) тогда можно делать минимум 2 способами, оба в этом примере:
Файл Unit1.cpp
//--------------------------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) < >//--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) < //extern int get1(); //Можно расскомментарить внешнее объявление extern и //закомментарить #include "Unit2.h" - тоже будет работать String s=get1(); ShowMessage (s); >
Файл Unit2.h — заголовочный файл 2-го модуля
//--------------------------------------------------------------------------- #ifndef Unit2H #define Unit2H int get1(); //Объявлен прототип функции //--------------------------------------------------------------------------- #endif
Файл Unit2.cpp — код 2-го модуля
//————————————————————————— #pragma hdrstop #include «Unit2.h» //————————————————————————— #pragma package(smart_init) int get1 ()
18.04.2011, 20:25 [18601 просмотр]
Unit2 h и unit2 cpp
Pers.narod.ru. Обучение. Примеры по C++ Builder. Подключаем модули и файлы к проекту C++ Builder
При открытом проекте (допустим, в нём есть модуль Unit1 и форма Form1 ) выполнить File->New->Form
Появился модуль Unit2 и форма Form2
В первом модуле ставим
#include «Unit2.h»
Или клавишами Alt+F11 (меню Files->Include Unit Hdr. )
После этого можем обращаться из первого модуля к данным второго.
Например, в Unit2.h написано:
#include #include #include //--------------------------------------------------------------------------- class TForm2 : public TForm < __published: // IDE-managed Components private: // User declarations public: // User declarations int iii=1; //Описали здесь свою переменную __fastcall TForm2(TComponent* Owner); >; //--------------------------------------------------------------------------- extern PACKAGE TForm2 *Form2; //--------------------------------------------------------------------------- #endif
Значит, Unit1.cpp (на его форме 1 кнопка Button1 и в коде 1 метод TForm1::Button1Click ) может быть таким:
//--------------------------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //Подключили второй модуль! //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) < >//--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) < String s; s=IntToStr(Form2->iii); //Использовали переменную из класса второй формы ShowMessage (s); >
Если мы подключили модуль без формы (меню File->New->Unit ) тогда можно делать минимум 2 способами, оба в этом примере:
Файл Unit1.cpp
//--------------------------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) < >//--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) < //extern int get1(); //Можно расскомментарить внешнее объявление extern и //закомментарить #include "Unit2.h" - тоже будет работать String s=get1(); ShowMessage (s); >
Файл Unit2.h — заголовочный файл 2-го модуля
//--------------------------------------------------------------------------- #ifndef Unit2H #define Unit2H int get1(); //Объявлен прототип функции //--------------------------------------------------------------------------- #endif
Файл Unit2.cpp — код 2-го модуля
//————————————————————————— #pragma hdrstop #include «Unit2.h» //————————————————————————— #pragma package(smart_init) int get1 ()