C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
TThread, Потоки мои,поотоки,уносли ноги,выручали
tvs_spb
Отправлено: 19.08.2004, 14:39


Ученик-кочегар

Группа: Участник
Сообщений: 20



Главная форма приложения запускает поток.
Поток обращается к компоненту,расположенному на гл.форме.
При Close() главной формы(где выполняется terminate()для потока) выдаётся ошибка памяти(я думаю из-за того что поток продолжает обращаться к гл.форме).
Эту ошибку удаётся избежать,если обращаться в потоке к гл.форме через Synchronize().Но тогда такие тормоза начинаются — врагу не пожелаешь!
Konstantine
Отправлено: 19.08.2004, 14:59


Мастер участка

Группа: Модератор
Сообщений: 545



ошибка эта из-за того что класс Thread уничтожается, а он ещё не закончил выполнение.

терминате — это не уничтожить поток — это подать сообщение на уничтожение, нада следующее:
1) в глав. форме — после терминате подай Thread1->WaitFor(); это дождётся завершения потока.
2) в потоке — в теле цикла или как там у тебя — проверку на флаг Terminated, при котором — иди к концу Execute. типа такого:

CODE
void Execute()
{
while(!Terminated)
{
... твои действия
}
}

или
CODE
void Execute()
{
... твои действия

if(Terminated) return;

... твои действия
}


Отредактировано Konstantine — 19/08/2004, 16:17
Guest
Отправлено: 19.08.2004, 15:01


Не зарегистрирован







Увы, за все надо платить.
А без Synchronize() увы, никак.

[QUOTE]Поток обращается к компоненту,расположенному на гл.форме

Или создавайте окно (для вывода результатов из потока),
без использования VCL, средствами API, тогда к этому окну
сможете обращаться без Synchronize().
Doga
Отправлено: 19.08.2004, 15:23


Мастер участка

Группа: Участник
Сообщений: 575



А что, когда закрывается главная форма, поток продолжает работать? ohmy.gif Как же тогда он завершается? Что, деструктор потока вообще не вызывается?


Я так понял, что когда закрывается главная форма, этот поток уже не нужен. Тогда в деструкторе формы как раз его и можно завершить и удалить. Или даже раньше  — в событии OnClose или OnCloseQuery. В этом случае не будет никаких проблем с потоком.



QUOTE

Эту ошибку удаётся избежать,если обращаться в потоке к гл.форме через Synchronize().


Однако, Вы смелый человек, если пытаетесь работать в поотоке с внеешними объектами без Synchronize(). smile.gif
Вы разве не обращали внимания на текст коментария, автоматически добавляемый BCB шаблон кода при создании нового класса TThread?

Типа такого:
CODE

//---------------------------------------------------------------------------
//   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 TModalWin1Thread::UpdateCaption()
//      {
//        Form1->Caption = "Updated in a thread";
//      }
//---------------------------------------------------------------------------

Тут вроде всё понятно написано... smile.gif

А на счёт тормозов — тут возможны 2 варианта.
1. Наиболее вероятно, что у потока установлен слишком высокий приоритет (Priority). Тут всё ясно — поток просто не даёт достаточного времени для работы основной проги. Надо снизить приоритет потока , напр. Priority = tpNormal;
2. Код потока не оптимален — тут разбираться лично Вам...

Konstantine
Отправлено: 19.08.2004, 15:38


Мастер участка

Группа: Модератор
Сообщений: 545



деструктор-то деструктором, но пока Execute не закончится — ничё вообще не будет, даже наоборот. а синхронизация — это для связи, без неё можно и обойтись но тормоза ужасные появляются.
Doga
Отправлено: 19.08.2004, 16:27


Мастер участка

Группа: Участник
Сообщений: 575



Насчёт Execute() — ето точно, пока она каким либо образом не завершится — поток не закроется.

А насчеёт Synchronize() — тут еще можно поспорить.

Вот примерчик, запустите его как-нибудь на досуге с разными значениями AShowMessageType. biggrin.gif

Unit2.h
CODE

//---------------------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class TShowMessageThread : public TThread
{
private:
protected:
bool FShowMessageType;
int FCount;

AnsiString FMessage;

void __fastcall ShowThreadMessage(void);
void __fastcall Execute();
public:
__fastcall TShowMessageThread(bool CreateSuspended, bool AShowMessageType);
};
//---------------------------------------------------------------------------
#endif


Unit2.cpp
CODE

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit2.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------

// 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 TShowMessageThread::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------

__fastcall TShowMessageThread::TShowMessageThread(bool CreateSuspended, bool AShowMessageType)
: TThread(CreateSuspended)
{
FShowMessageType = AShowMessageType;

FCount = 0;

FMessage = "Сообщение №";

if (!CreateSuspended)
{
Resume();
}
}
//---------------------------------------------------------------------------
void __fastcall TShowMessageThread::Execute()
{
//---- Place thread code here ----
for (int k = 0;; k++)
{
if (!Terminated)
{
FCount = k + 1;

if (!FShowMessageType)
{
MessageBox(NULL, AnsiString(FMessage + IntToStr(FCount)).c_str(), "ИНФО",MB_OK + MB_ICONINFORMATION + MB_APPLMODAL);
}
else
{
Synchronize(ShowThreadMessage);
}
}
else
{
break;
}
}
}
//---------------------------------------------------------------------------
void __fastcall TShowMessageThread::ShowThreadMessage(void)
{
MessageBox(NULL, AnsiString(FMessage + IntToStr(FCount)).c_str(), "ИНФО",MB_OK + MB_ICONINFORMATION + MB_APPLMODAL);
}
//---------------------------------------------------------------------------


P.S. Я постоянно использую Synchronize() и никаких тормозов ИЗ-ЗА НЕЁ ещё не замечал smile.gif

Отредактировано Doga — 19/08/2004, 17:35
tvs_spb
Отправлено: 19.08.2004, 17:14


Ученик-кочегар

Группа: Участник
Сообщений: 20



Вот как это пользуется
[CODE]

void __fastcall TQThread::Execute()
{
FreeOnTerminate = true;
while (!Terminated)
{
for(i=0;;i++)
Synchronize(CheckRecord);
}
}
//---------------------------------------------------------------------------

void __fastcall TQThread::CheckRecord()
{
if(formQuery->lvQuery->Items->Count)
{
if(i>= formQuery->lvQuery->Items->Count) i=0;
formQuery->lvQuery->Selected = formQuery->lvQuery->Item->Item[i];
}
}
//---------------------------------------------------------------------------

[CODE]

и тут такие тормоза,что мама не горюй!!!
приоритет потока средний
tvs_spb
Отправлено: 19.08.2004, 17:21


Ученик-кочегар

Группа: Участник
Сообщений: 20



а что касается Thread()->WaitFor();
этот метод напрочь вешает прогу.

void __fastcall TformQuery::Close()
{
Thread()->Terminate();
Thread()->WaitFor();
}
где же тут собака порылась???
Doga
Отправлено: 19.08.2004, 19:47


Мастер участка

Группа: Участник
Сообщений: 575



Как Вы думаете, сколько раз в теле функции Execute() проверяется значение свойства Terminated?


CODE

void __fastcall TQThread::Execute()
{
FreeOnTerminate = true;

for(i=0;;i++)
{
if (!Terminated)
{
Synchronize(CheckRecord);
}
   else
   {
     break;
   }
}
}

Может быть в таком варианте опрос этого свойства будет чаще? biggrin.gif

Далее.
У меня большое подозрение, что значение свойства formQuery->lvQuery->Items->Count вычисляется каждый раз по новой, при каждом обращении к нему. А что если так:

CODE

void __fastcall TQThread::Execute()
{
FreeOnTerminate = true;

//Переменная int FItemsCount должна быть объявлена как свойство класса TQThread
FItemsCount = formQuery->lvQuery->Items->Count;

for(i=0;;i++)
{
if (!Terminated)
{
Synchronize(CheckRecord);
}
   else
   {
     break;
   }
}
}
//---------------------------------------------------------------------------
void __fastcall TQThread::CheckRecord()
{
if (FItemsCount)
{
if (i >= FItemsCount)
{
i=0;
}

formQuery->lvQuery->Selected = formQuery->lvQuery->Item->Item[i];
}
}


Правда, для этого варианта надо быть уверенным, что фактическое значение formQuery->lvQuery->Items->Count, по крайней мере не уменьшилось за время работы TQThread::Execute()
smile.gif

Попробуйте...

Отредактировано Doga — 19/08/2004, 21:04
Konstantine
Отправлено: 20.08.2004, 08:58


Мастер участка

Группа: Модератор
Сообщений: 545



QUOTE (tvs_spb @ 19/08/2004, 18:23)
а что касается Thread()->WaitFor();
этот метод напрочь вешает прогу.

Да, он вешает, но только до тех пор, пока не завершится поток.
Проверь:
CODE
void __fastcall TForm1::Button1OnClick(TObject *)
{
 TThread1 thr1=new TThread1(false);
 thr->WaitFor();
 thr->Free();
 ShowMessage("Поток завершен");
}

а в теле потока:
CODE
void Execute()
{
 Pause(5000);  //сделать паузу на ~5 сек, вроде бы так, но не уверен
}

когда нажмёшь на кнопку, через 5 сек выскочит сообщение "Поток завершен"
Konstantine
  Отправлено: 20.08.2004, 09:04


Мастер участка

Группа: Модератор
Сообщений: 545



кстати, предлагаю принять решение о том, что-бы убирать комментарии, которые компилятор вставляет сам — они стандартные и никому здесь не нужны — а только загромождают место
timson
Отправлено: 20.08.2004, 10:19


Станционный диспетчер

Группа: Участник
Сообщений: 82



даа, совсем стали забывать об API (наверно речь не идет о кроссплатформенности (CLX) в данном случае)
завершить принудительно поток:
CODE
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

употреблять осторожно , если используется синхронизация, обращение к ядру ОС (не использовать желательно)

а вообще с потоками на API:
- создать поток:
CODE
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId);

где lpStartAddres — указатель на функцию (которую поток выполняет):
CODE
DWORD WINAPI ThreadProc(LPVOID lpParameter);

- завершить принудительно поток:
CODE
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

- handle потока удалить (не завершает работу потока):
CODE
BOOL CloseHandle(HANDLE hObject);


QUOTE
А без Synchronize() увы, никак.

можно, но только осторожно. надо тогда, когда обращаемся из более одного потока (сам процесс тоже поток) к одной области памяти, когда кто-то пишет, а кто-то читает ее.
если компонент (даже только свойство его (не все)) изменяются только из потока, то можно спокойно., да и вообще можно, но плохой стиль, хотя в некоторых случаях целесообразнее..
timson
Отправлено: 20.08.2004, 14:32


Станционный диспетчер

Группа: Участник
Сообщений: 82



а потоки трудно отлаживать (иногда невозможно)... приложение и дебагер виснут почти, CPU не нагружен.
Konstantine
Отправлено: 20.08.2004, 14:42


Мастер участка

Группа: Модератор
Сообщений: 545



просто сначала надо отладить дополнит. поток, а потом основной.
BreakPoint-ы в потоке тоже работают. а зависания обычно образуются при неправильной синхронизации, попытке доступа из потока к компонентам формы, или при неправильном создании/удалении потока.

ну и конечно не исключено зацикливание потока :-(

а так если всё делать не спеша и с соблюдением логики (типа создал-удалил), то всё будет ОК.

P.S.: у меня потоки обычно зависают при необработаных Exception-ах и на запрещ. операциях.
klen
Отправлено: 21.08.2004, 00:20


Машинист паровоза

Группа: Участник
Сообщений: 239



Мужики, мое скромное мнение такое VCL — хорошая библиотека, но с потоками Borland зря так ... Я всегда пользуюсь в этом случае АPI, а проблему взаимодействия форм и потоков решаю через сообщения и каналы.

Вообщето отделятьвсегда надо интерфейс (главный поток) от собственно алгоритмов вычислений(другие потоки).

Вернуться в Вопросы программирования в C++Builder