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
|
А что, когда закрывается главная форма, поток продолжает работать? Как же тогда он завершается? Что, деструктор потока вообще не вызывается?
Я так понял, что когда закрывается главная форма, этот поток уже не нужен. Тогда в деструкторе формы как раз его и можно завершить и удалить. Или даже раньше — в событии OnClose или OnCloseQuery. В этом случае не будет никаких проблем с потоком.
QUOTE |
Эту ошибку удаётся избежать,если обращаться в потоке к гл.форме через Synchronize().
|
Однако, Вы смелый человек, если пытаетесь работать в поотоке с внеешними объектами без Synchronize().
Вы разве не обращали внимания на текст коментария, автоматически добавляемый 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";
// }
//---------------------------------------------------------------------------
|
Тут вроде всё понятно написано...
А на счёт тормозов — тут возможны 2 варианта.
1. Наиболее вероятно, что у потока установлен слишком высокий приоритет (Priority). Тут всё ясно — поток просто не даёт достаточного времени для работы основной проги. Надо снизить приоритет потока , напр. Priority = tpNormal;
2. Код потока не оптимален — тут разбираться лично Вам...
|
|
Konstantine |
Отправлено: 19.08.2004, 15:38 |
|
Мастер участка
Группа: Модератор
Сообщений: 545
|
деструктор-то деструктором, но пока Execute не закончится — ничё вообще не будет, даже наоборот. а синхронизация — это для связи, без неё можно и обойтись но тормоза ужасные появляются.
|
|
Doga |
Отправлено: 19.08.2004, 16:27 |
|
Мастер участка
Группа: Участник
Сообщений: 575
|
Насчёт Execute() — ето точно, пока она каким либо образом не завершится — поток не закроется.
А насчеёт Synchronize() — тут еще можно поспорить.
Вот примерчик, запустите его как-нибудь на досуге с разными значениями AShowMessageType.
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() и никаких тормозов ИЗ-ЗА НЕЁ ещё не замечал
Отредактировано 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;
}
}
}
|
Может быть в таком варианте опрос этого свойства будет чаще?
Далее.
У меня большое подозрение, что значение свойства 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()
Попробуйте...
Отредактировано 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, а проблему взаимодействия форм и потоков решаю через сообщения и каналы.
Вообщето отделятьвсегда надо интерфейс (главный поток) от собственно алгоритмов вычислений(другие потоки). |
|