link6958 link6959 link6960 link6961 link6962 link6963 link6964 link6965 link6966 link6967 link6968 link6969 link6970 link6971 link6972 link6973 link6974 link6975 link6976 link6977 link6978 link6979 link6980 link6981 link6982 link6983 link6984 link6985 link6986 link6987 link6988 link6989 link6990 link6991 link6992 link6993 link6994 link6995 link6996 link6997 link6998 link6999 link7000 link7001 link7002 link7003 link7004 link7005 link7006 link7007 link7008 link7009 link7010 link7011 link7012 link7013 link7014 link7015 link7016 link7017 link7018 link7019 link7020 link7021 link7022 link7023 link7024 link7025 link7026 link7027 link7028 link7029 link7030 link7031 link7032 link7033 link7034 link7035 link7036 link7037 link7038 link7039 link7040 link7041 link7042 link7043 link7044 link7045 link7046 link7047 link7048 link7049 link7050 link7051 link7052 link7053 link7054 link7055 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
такси из Москвы в Туапсе такси из Туапсе в Москву


Разработка многопоточных приложений в 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).