Розробка багатопоточних додатків в c builder

Розробка багатопоточних додатків в C ++ Builder

C ++ Builder надає кілька об'єктів, які роблять розробку багатопотокових додатків простіше.
Для створення багатопоточних додатків в C ++ Builder реалізований абстрактний клас TThread.
TThread - абстрактний клас, який допускає створення окремих потоків виконуються в додатку.
Створіть нащадка класу TThread, щоб представити виконується потік в багатопотоковому додатку.
Кожен новий екземпляр нащадка TThread - новий потік виконання.
Безліч екземплярів, отримані від класу TThread. робить C ++ Builder багатопотоковим додатком.

Ви повинні створити новий об'єкт класу, похідний від TThread.
Для цього:

Виберіть File | New | Other | Thread Object. щоб створити новий модуль, що містить об'єкт, похідний від класу TThread,
вам запропонують якось назвати цей клас, назвіть як вам подобається (наприклад TMyThread)
Буде створено новий модуль, що містить опис цього класу TMyThread, його конструктор і метод Execute ()

В основній програмі створіть об'єкт цього потокового класу
// створюємо потік в загальмованому стані (true), запущеному (false)

TMyThread * Thr = new TMyThread (true); // в загальмованому

Якщо ви помітили, в конструкторі є параметр bool CreateSuspended. якщо при створенні
об'єкта цей параметр має значення false. потік відразу - при створенні об'єкта почне свою роботу,
тобто почнеться виконання коду в методі Execute (), якщо параметр bool CreateSuspended true. буде створено
потік в загальмованому стані, для запуску потоку вам потрібно застосувати методом Resume ()
Thr-> Resume ();

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

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

Пріоритети можуть мати наступні значення:

потік отримує найвищий пріоритет

Призупинити потік можна методом 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 метод для наступних об'єктів:
  • Компоненти Data access потоко-безпечні такі: Для BDE-доступних наборів даних, кожен потік повинен мати власний компонент сеансу бази даних. Один виняток до цього - то, коли Ви використовуєте драйвери Microsoft Access. які сформовані, використовуючи бібліотеку Microsoft, яка потоко-безпечна. Для dbDirect. поки бібліотека клієнта потоко-безпечна, dbDirect компоненти будуть потоко-безпечні.
    ADO і InterbaseExpress компоненти потоко-безпечні.
    При використанні в потоках компонентів data access, Ви повинні бути уважніше.
    Таким чином, наприклад, Ви повинні викликаючи Synchronize, які пов'язують data control з dataset. встановлюючи властивість DataSet об'єкта data source. але Ви не повинні використовувати Synchronize, щоб звернутися до даних field в dataset.
    Для докладної інформації про використання сеансів бази даних з потоками в додатках BDE-enabled, см. Managing multiple sessions в Help до C ++ Builder.
  • DataCLX об'єкти потоко-безпечні, хоча об'єкти VisualCLX - немає.
  • Graphics об'єкти потоко-безпечні. Ви не повинні використовувати головний VCL або CLX потік, щоб звернутися до TFont, TPen, TBrush, TBitmap, TMetafile (VCL тільки), TDrawing (CLX тільки), або TIcon. Об'єкти Canvas можуть використовуватися поза Synchronize методу, необхідно тільки блокувати їх перед застосуванням і звільняти після,
    для цього використовуються методи Lock () і Unlock (). наприклад так:
    // ---
    Form1-> Canvas-> Lock ();
    for (int i = 0; i<5000; i++) Form1->Canvas-> TextOut (20,20, i);
    Form1-> Canvas-> Unlock ();
    // ---
  • У той час як об'єкти списку list НЕ потоко-безпечні,
    Ви можете використовувати потоко-безпечну версію, TThreadList. замість TList.

Крім координації роботи потоків за допомогою пріоритетів потоків в додатку також часто буває необхідне синхронізувати потоки. Що мається на увазі.
Координація спільної роботи декількох потоків, якщо наприклад вони намагаються одночасно щось зробити,
наприклад вивести що-небудь на форму, отримати доступ до глобальних даними і т.д.
Для цього використовуються такі об'єкти, як критичні розділи (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); // видалити критичний розділ
>

М'ютекси виконуються повільніше критичних розділів, проте вони мають більші можливості,
ніж критичні розділи. Так, наприклад, вони можуть використовуватися різними процесами.
Створюються вони за допомогою 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 =;
__property bool Terminated =;

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 =;
__property bool FreeOnTerminate =;
__property unsigned Handle =;
__property TThreadPriority Priority =;
__property bool Suspended =;
__property unsigned ThreadID =;
__property TNotifyEvent OnTerminate =;
>;


Основні властивості і методи класу TThread

Схожі статті