link7056 link7057 link7058 link7059 link7060 link7061 link7062 link7063 link7064 link7065 link7066 link7067 link7068 link7069 link7070 link7071 link7072 link7073 link7074 link7075 link7076 link7077 link7078 link7079 link7080 link7081 link7082 link7083 link7084 link7085 link7086 link7087 link7088 link7089 link7090 link7091 link7092 link7093 link7094 link7095 link7096 link7097 link7098 link7099 link7100 link7101 link7102 link7103 link7104 link7105 link7106 link7107 link7108 link7109 link7110 link7111 link7112 link7113 link7114 link7115 link7116 link7117 link7118 link7119 link7120 link7121 link7122 link7123 link7124 link7125 link7126 link7127 link7128 link7129 link7130 link7131 link7132 link7133 link7134 link7135 link7136 link7137 link7138 link7139 link7140 link7141 link7142 link7143 link7144 link7145 link7146 link7147 link7148 link7149 link7150 link7151 link7152 link7153 link7154 link7155 link7156 link7157 link7158 link7159 link7160 link7161 link7162 link7163 link7164 link7165 link7166 link7167 link7168 link7169 link7170 link7171 link7172 link7173 link7174 link7175 link7176 link7177 link7178 link7179 link7180 link7181 link7182 link7183 link7184 link7185 link7186 link7187 link7188 link7189 link7190 link7191 link7192 link7193 link7194 link7195 link7196 link7197 link7198 link7199
такси из Москвы в Туапсе такси из Туапсе в Москву


Разработка многопоточных приложений в C++Builder

C++Builder предоставляет несколько объектов, которые делают разработку многопоточных приложений проще.
Для создания многопоточных приложений в C++Builder реализован абстрактный класс TThread.
TThread — абстрактный класс, который допускает создание отдельных потоков выполняющихся в приложении.
Создайте потомка класса TThread, чтобы представить выполняемый поток в многопоточном приложении.
Каждый новый экземпляр потомка TThread — новый поток выполнения.
Множество экземпляров, полученные от класса TThread , делает C++Builder многопоточным приложением.

Вы должны создать новый объект класса, производный от TThread.
Для этого:

Выберите File | New | Other | Thread Object , чтобы создать новый модуль, содержащий объект, производный от класса TThread,
вам предложат как-то назвать этот класс, назовите как вам нравится (например TMyThread)
Будет создан новый модуль, содержащий описание этого класса TMyThread, его конструктор и метод Execute()

__fastcall TMyThread::TMyThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
// B метод объекта Execute(), вставьте код, который должен выполняться, когда поток выполняется.
void __fastcall TMyThread::Execute()
{
//---- Place thread code here ----
}
//---------------------------------------------------------------------------


В основной программе создайте объект этого потокового класса
// создаем поток в приостановленном состоянии (true), запущенном (false)

TMyThread *Thr = new TMyThread(true); // в приостановленном

Если вы заметили, в конструкторе есть параметр bool CreateSuspended, если при создании
объекта этот параметр имеет значение false, поток сразу — при создании объекта начнет свою работу,
то есть начнется выполнение кода в методе Execute(), если параметр bool CreateSuspended true, будет создан
поток в приостановленном состоянии, для запуска потока вам требуется применить методом Resume()
Thr->Resume();

Также, например в конструкторе, Вы можете указать(изменить) приоритет потока  — то есть сколько процессорного времени
будет выделять операционная система для вашего потока, относительно других потоков вашего приложения
- за это отвечает свойство Priority, по умолчанию все потоки создаются с нормальным приоритетом.

Thr->Priority = tpLower; // установить приоритет ниже нормального
Thr->Resume();                        // запустить поток выполняться


Приоритеты могут иметь следующие значения:

Значение Приоритет
tpIdle поток выполняется только, когда система — простаивает. Windows не будет прерывать другие потоки, чтобы выполнить поток с tpIdle приоритетом.
tpLowest приоритет потока — на два пункта ниже нормального.
tpLower приоритет потока — на один пункт ниже нормального.
tpNormal нормальный приоритет
tpHigher приоритет потока — на один пункт выше нормального.
tpHighest приоритет потока — на два пункта выше нормального.
tpTimeCritical поток получает самый высокий приоритет

Приостановить поток можно методом Suspend(), запустить поток методом Resume()
Выполнение потока автоматически завершается после завершения функции Execute() или закрытии приложения.

Чтобы занятая потоком память освобождалась при завершении потока используйте в Execute() FreeOnTerminate = true;
Однако, возможны ситуации, когда завершение потока должно быть скоординировано с другим потоком. Например, Вы должны ждать возвращения значения из одного потока, чтобы возвратить это значение в другой поток. Чтобы сделать это, Вы не освобождаете первый поток, пока второй поток не получит возвращаемое значение. Вы можете обработать эту ситуацию, установив FreeOnTerminate=false и затем явно освободив первый поток из второго.

Чтобы прекратить выполнение потока, не дожидаясь его завершения, например из другого потока, используйте метод Terminate().
Thr->Terminate();
Метод Terminate() задает значение true для свойства Terminated, то есть Вам самому необходимо в потоке
(в методе Execute) периодически проверять значение Terminated и если это значение стало true,
предприянять необходимые действия, например завершить поток, т.е. выйти из Execute()
Например, так

void __fastcall TMyThread::Execute()
{
     FreeOnTerminate = true; // освободить занятую потоком память по окончании его работы
     for(int i=0; i<10000; i++)
     {
      // -- какие-то сложные вычисления в цикле
      if(Terminated) break; // прервать- завершить поток
     }
}

В экстремальных ситуациях, для завершения работы потока используйте API-функцию TerminateThread().
Эта функция закрывает текущий поток без освобождения памяти, занятой потоком процесса.
Синтаксис ее: TerminateThread((HANDLE)Thr->Handle, false);

Теперь об особенностях работы потоков в приложениях C++Builder ( бибилиотека VCL )

Как известно при написании программ на C++Builder (и Delphi) обычно вы пользуетесь бибилиотекой VCL.
(например компонентами из палитры компонентов)
Когда Вы используете объекты из иерархий VCL или CLX, их свойства и методы, не гарантируется безопасность потока.
То есть, обращаясь к свойствам или выполняя методы этих объектов, могут выполнятся некоторые действия, которые используют память, которая не защищена от действий других потоков.
А значит, основной поток библиотеки VCL должен быть единственным потоком, управляющим этой библиотекой.
(он-же является первичным потоком вашего приложения)
Он обрабатывает все сообщения Windows, полученные компонентами в вашем приложении.
Как-же тогда безопасно из потока получить доступ к управлению свойствами и методами VCL-объектов (компонентов) ?
Для этого в TThread предусмотрен метод Synchronize()

void __fastcall TMyThread::Execute()
{
FreeOnTerminate = true; // освободить занятую потоком память по окончании его работы
for(int i=0; i<10000; i++)
{
// -- какие-то сложные вычисления в цикле
// ---
if(Terminated) break; // прекратить извне поток
Synchronize(pb); // позволяет получить доступ к свойствам и методам VCL-объектов
}
}
//---------------------------------------------------------------------------

void __fastcall TMyThread::pb()
{
static int n = 0;

n++;
Form1->Label1->Caption = n;
Application->ProcessMessages();
}
//-----


Обратите внимание: Поскольку Synchronize использует цикл сообщений, это не работает в консольных приложениях. Вы должны использовать другие механизмы, типа критических разделов, для защиты доступа к объектам VCL или CLX в консольных приложениях

Не надо использовать Synchronize метод для следующих объектов:

Синхронизация потоков.

Помимо координации работы потоков с помощью приоритетов потоков в приложении также часто бывает необходимым синхронизировать потоки. Что имеется ввиду ?
Координация совместной работы нескольких потоков, если например они пытаются одновременно что-то сделать,
например вывести что-либо на форму, получить доступ к глобальным данным и т.д.
Для этого используются такие объекты, как критические разделы ( critical section ), мьютексы ( mutex ) ,
семафоры ( semaphore ), таймеры.

Критические разделы. ( critical section )

Для создания и использования критического раздела, нужно объявить переменную типа CRITICAL_SECTION,
в нашем примере в Unit1.h
...
public: // User declarations
      CRITICAL_SECTION CS;
...

// Потом эту переменную CS нужно инициализировать (создать критический раздел)
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
     InitializeCriticalSection(&CS);
}


// используем критический раздел в потоке, когда нужно блокировать доступ к данным
void __fastcall TMyThread::Execute()
{
   FreeOnTerminate = true; // освободить занятую потоком память по окончании его работы
   for(int i=0; i<10000; i++)
   {
     // -- какие-то сложные вычисления в цикле
    if(Terminated) break; // прекратить извне поток
     EnterCriticalSection(&Form1->CS); // блокировать доступ к данным (войти в критический раздел)
     ...  доступ к глобальным данным
     LeaveCriticalSection(&Form1->CS); // закрыть критический раздел (покинуть критический раздел)
   }
}


Когда критический раздел становиться не нужен, удаляем его
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
     DeleteCriticalSection(&CS); // удалить критический раздел
}


Мьютексы. ( mutex )

Мьютексы выполняются медленнее критических разделов, однако они обладают большими возможностями,
чем критические разделы. Так, например, они могут использоваться разными процессами.
Создаются они с помощью API-функции CreateMutex(), работа с ними также осуществляется с помощью
API-функций, таких как WaitForSingleObject(), ReleaseMutex(), и др.
Об использовании мьютексов и семафоров см. Разработка многопоточных приложений в Windows.


Описание класса TThread ( Находится в Classes.hpp )

class DELPHICLASS TThread;
class PASCALIMPLEMENTATION TThread : public System::TObject
{
typedef System::TObject inherited;

private:
unsigned FHandle;
unsigned FThreadID;
bool FCreateSuspended;
bool FTerminated;
bool FSuspended;
bool FFreeOnTerminate;
bool FFinished;
int FReturnValue;
TNotifyEvent FOnTerminate;
TThreadMethod FMethod;
System::TObject* FSynchronizeException;
System::TObject* FFatalException;
void __fastcall CheckThreadError(int ErrCode)/* overload */;
void __fastcall CheckThreadError(bool Success)/* overload */;
void __fastcall CallOnTerminate(void);
TThreadPriority __fastcall GetPriority(void);
void __fastcall SetPriority(TThreadPriority Value);
void __fastcall SetSuspended(bool Value);

protected:
virtual void __fastcall DoTerminate(void);
virtual void __fastcall Execute(void) = 0 ;
void __fastcall Synchronize(TThreadMethod Method);
__property int ReturnValue = {read=FReturnValue, write=FReturnValue, nodefault};
__property bool Terminated = {read=FTerminated, nodefault};

public:
__fastcall TThread(bool CreateSuspended);
__fastcall virtual ~TThread(void);
virtual void __fastcall AfterConstruction(void);
void __fastcall Resume(void);
void __fastcall Suspend(void);
void __fastcall Terminate(void);
unsigned __fastcall WaitFor(void);
__property System::TObject* FatalException = {read=FFatalException};
__property bool FreeOnTerminate = {read=FFreeOnTerminate, write=FFreeOnTerminate, nodefault};
__property unsigned Handle = {read=FHandle, nodefault};
__property TThreadPriority Priority = {read=GetPriority, write=SetPriority, nodefault};
__property bool Suspended = {read=FSuspended, write=SetSuspended, nodefault};
__property unsigned ThreadID = {read=FThreadID, nodefault};
__property TNotifyEvent OnTerminate = {read=FOnTerminate, write=FOnTerminate};
};



Основные свойства и методы класса TThread

Свойство Описание
FreeOnTerminate Указывает, будет ли объект потока автоматически удален при завершении работы потока
Handle Дескриптор потока, для вызова API-функций
Priority Задает приоритет потока
ReturnValue Определяет значение, возвращаемое другим потокам, после завершения текущего потока
Suspended Указывает, приостановлено-ли выполнение потока
Terminated Определяет, может ли прекращаться выполнение потока
ThreadID Определяет идентификатор потока

Метод Описание
DoTerminate() Вызывает обработчик события OnTerminate() без прекращения работы потока
Execute() Содержит код, который будет выполнен при запуске потока
Resume() Возобновляет работу приостановленного потока
Suspend() Приостанавливает работу потока
Synchronize() Выполняет вызов в первичном потоке библиотеки VCL
Terminate() Сигнализирует о прекращении выполнения потока
WaitFor() Ждет прекращения работы потока
есть и другие менее используемые свойства и методы.

Предупреждение: исключения, которые возбуждены, но не перехвачены в TThread потомке при выполнении в методе Execute могут стать причиной access violations, когда Вы выполняете приложение вне интегрированной среды разработки. Вы можете принять меры против этого access violation, обеспечив метод Execute блоком try.._finally, который включает тело этого метода.

Пример работы простого многопоточного приложения (C++Builder 6).