Скачиваний:
100
Добавлен:
01.05.2014
Размер:
1.56 Mб
Скачать

Класс tThread

В ряде случаев в приложении желательно организовать несколько потоков (нитей), выполняющихся одновременно. Например, одна нить выполнения может осуществлять основную работу, а вторая, с меньшим приоритетом, может в то же время готовить или реорганизовывать какие-то файлы, рисовать изображения, которые потребуются в дальнейшем, т.е. выполнять черновую работу. Другой пример - параллельная работа с несколькими внешними источниками информации. Особенно большой выигрыш в производительности за счет параллельного выполнения нескольких нитей можно получить в многопроцессорных системах, в которых можно организовать выполнение каждой нити отдельным процессором.

Параллельно выполняемые нити работают в адресном пространстве одного процесса и могут иметь доступ к глобальным переменным этого процесса.

Одним из способов создания приложения с несколькими потоками является использование компонента типа TThread. Этот компонент отсутствует в палитре библиотеки.TThread- это абстрактный класс, позволяющий создать в приложении отдельную нить выполнения. Для того чтобы ввестиTThreadв свое приложение, надо выполнить команду \"File New Other\" и в открывшемся окне репозитария на странице "New" выбрать пиктограмму "Thread Object". Вам будет задан вопрос об имени (Class Name) создаваемого класса, наследующегоTThread. Укажите любое имя (например,Т) и в ваш проект добавится новый модуль, файлы которого имеют вид:

// заголовочный файл Unit2.h:

#ifndef Unit2H

#define Unit2H

#include <Classes.hpp>

class T : public TThread

{

private:

/* Добавляемые здесь и в разделе protected свойства и методы будут

* доступны только внутри класса */

protected:

void _ fastcall Execute();

public:

_ fastcall Т (bool CreateSuspended);

/* Добавляемые здесь свойства и методы будут доступны

* для функций приложения через объект нити */

}

#endif

// файл реализации Unit2.cpp:

#include <vcl.h>

#include "Unit2.h"

// Important: Methods and properties of objects in VCL

// can only be used in a method

// called using Synchronize, for example:

// Synchronize (UpdateCaption);

// where UpdateCaption could look like:

// void __fastcall Unit2::UpdateCaption ()

// {

// Form1->Caption = "Updated in a thread";

// }

/* Важно: методы и свойства объектов VCL могут использоваться

* только с применением метода, который вызывается методом

* Synchronize, например

* Synchronize (UpdateCaption);

* где метод UpdateCaption может иметь вид:

* void __fastcall Unit2::UpdateCaption ()

* {

* Form1->Caption = "Updated in a thread";

* } */

__fastcall T::T(bool CreateSuspended)

: TThread (CreateSuspended)

{

}

void __fastcall T::Execute()

{

// Place thread code here

// Здесь размещается код потока

}

В приведенный выше текст добавлен перевод комментариев, которые С++Builder помещает в модуль, а также введены комментарии, поясняющие область видимости вводимых вами новых свойств и методов.

Созданный C++Builder модуль, как вы можете видеть, содержит заготовку класса с введенным вами именем (в нашем пример - Т), наследующегоTThread. Вы можете добавлять в него любые свойства и методы, учитывая отмеченные В комментариях области видимости. ПроцедураExecute, заготовку которой вы можете видеть в коде, является основной процедурой нити. При ее окончании завершается и выполнение данной нити приложения.

Класс наследует от TThreadряд методов. Прежде всего это конструктор, создающий объект нити:

__fastcall TThread(bool CreateSuspended);

Параметр CreateSuspendedконструктора определяет способ выполнения нити. ЕслиCreateSuspended=false, то выполнение процедурыExecuteначинается немедленно после создания объекта. ЕслиCreateSuspended=true, то выполнение начнется только после того, как будет вызван методResume:

void __fastcall Resume (void);

Конструктор TThreadне должен вызываться в приложении явным образом. Для создания объекта классаTThread, как и для всех классов VCL, надо использовать операциюnew. Например:

Т *SecondProcess = new T(true);

SecondProcess->Resume();

Функция Resumeимеет две области применения. Во-первых, она запускает выполнение, если объект нити был создан сCreateSuspended=true. Во-вторых, она запускает приложение, приостановленное ранее методомSuspend:

void __fastcall Suspend(void);

Таким образом, вы можете в любой момент приостановить выполнение нити методом Suspend, а затем продолжить выполнение методомResume. Если вы подряд несколько раз вызвали методSuspend, то выполнение возобновится только после того, как столько же раз будет вызван методResume. Узнать, является ли нить приостановленной, можно по значению булева свойстваSuspended.

В функции Executeможно непосредственно писать операторы выполнения, вызовы каких-то иных функций и т.п. Однако если функция должна вызывать какие-то методы компонентов VCL или обращаться к свойствам формы, то необходимо соблюдать осторожность, поскольку при этом не исключены конфликты между параллельно выполняемыми нитями. В этом случае в функции надо вызывать методSynchronize, как сказано в комментарии, который вы могли видеть в приведенной выше заготовке модуля. Метод определен следующим образом:

typedef void __fastcall (__closure *TThreadMethod) (void);

void __fastcall Synchronize (TThreadMethod &bMethod);

В этом определении Method- функция, работающая с компонентами VCL. Таким образом, при работе с компонентами VCL надежнее строить выполнение следующим образом. Вы пишете функцию, выполняющую необходимые действия с компонентами VCL. Пусть вы дали ей имяWork. Тогда вы включаете ее объявление в класс нити, например, в разделprivate, даете в файле реализации ее описание, а процедураExecuteв этом случае может, например, состоять из единственного оператораSynchronize(Work):

// заголовочный файл Unit2.h

class Т : public TThread

{

private:

void __fastcall Work(void);

protected:

void __fastcall Execute();

public:

__fastcall T (bool CreateSuspended);

};

// файл реализации Unit2.cpp

void __fastcall T::Execute()

{

Synchronize (Work);

}

void __fastcall T::Work(void)

{

...

}

При завершении процедуры Executeпроисходит нормальное завершение выполнения нити. Однако возможно и досрочное завершение выполнения нити. Для этого в ее процедуруExecuteдолжна быть введена проверка булева свойстваTerminated(завершено). Нормально это свойство равноfalse. Но если какая-то внешняя нить вызвала методTerminateобъекта данной нити, тоTerminatedстановится равнымtrue. Если предполагается возможность такого завершения выполнения нити, то процедураExecuteдолжна периодически проверять значениеTerminatedи при получении значения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. Это свойство может иметь следующие значения:

Table 5: Возможные значения свойстваPriority

tpIdle

Нить выполняется только когда система свободна.

tpLowest

Приоритет нити на два пункта ниже нормального.

tpLower

Приоритет нити на один пункт ниже нормального.

tpNormal

Нормальный приоритет нити.

tpHigher

Приоритет нити на один пункт выше нормального.

tpHighest

Приоритет нити на два пункта выше нормального.

tpTimeCritical

Нить имеет наивысший приоритет.