Потоки. Synchronize -что за зверь такой и с чем едят?
Метод необходим в том случае если вам нужно организовать доступ потока к компоненту на форме. Так как при этом окно билдера имеет свой поток, метод предназначен для синхронизации вашего потока с потоком формы(говоря упрощенно). Соответственно и вызывать его необходимо в этом случае. Стандартным примером может служить изменение текста компонента TLabel в потоке и т.д.
Не нужно вызывать синхронизацию при работе с не-VCL классами, в этом случае необходимо использовать стандартные методы организации потокобезопасного доступа. Не нужно вызывать соответственно метод если вы работаете с переменными и классами которые создаются для каждого потока отдельно.
Кроме того надо иметь ввиду что метод достаточно тормознутый.
И еще вопросик...
Допустим у меня есть форма, на ней размещен компонент Memo
В исходнике, сначала я добавляю в memo строчку "До потока", потом создаю поток, который с некоторой задержкой добавляет в это Memo свою строку "из потока", а потом после вызова потока, в основном коде программы я добавляю в Memo третью строчку "после потока".
Нужно сделать так, чтоб третья строчка (после потока) не выводилась бы до того, как не закончит работу поточная функция.
Делал с Critical Section, но все равно, последняя строчка не дожидается вывода из потока.
Как можно с наименьшими ресурсными затратами организовать синхронизацию дополнительного и основного потока?
Если класс является полем класса формы то для него тоже можно использовать Synchronize.
И еще вопросик...
Допустим у меня есть форма, на ней размещен компонент Memo
В исходнике, сначала я добавляю в memo строчку "До потока", потом создаю поток, который с некоторой задержкой добавляет в это Memo свою строку "из потока", а потом после вызова потока, в основном коде программы я добавляю в Memo третью строчку "после потока".
Нужно сделать так, чтоб третья строчка (после потока) не выводилась бы до того, как не закончит работу поточная функция.
Делал с Critical Section, но все равно, последняя строчка не дожидается вывода из потока.
Как можно с наименьшими ресурсными затратами организовать синхронизацию дополнительного и основного потока?
Ну а в чем проблема - в основной функции потока перед завершением или посылай сообщение форме, или вызывай какую либо акцию. Найболее верно (просто) - послать форме сообщение о том что данный поток (функция) работу завершил. Все что для этого нужно - передать в функцию потока указатель на HWND.
{BCB}\CBuilder6\Examples\Apps\Threads
возможно многие ответы на вопросы ты там найдешь.
По посылке сообщений из потока на форуме примеры приводились неоднократно - поиск тебе в этом поможет. Поэтому код не считаю нужным приводить здесь еще раз.
Весь вопрос как приостановить выполнение основного кода программы, пока не будет выполнен код потока? Critical Section для этого существует или нет? Что то у меня не получается с ней синхронизировать поток с основным кодом. Если она не для этого, то на кой она вообще нужна?
Немного конкретики:
Я в отдельном потоке запускаю одну функцию (ф-ию поиска устройств). Она довольно ресурсоемкая и чтоб не загонять в ступор всю программу, я пускаю ее отдельным потоком. Но дело в том, что после вызова потока, дальнейший основной код должен работать с памятью, которая будет заполнена в коде потока. Соответственно нужно это дело как то синхронизировать, чтоб не было преждевременного обращения к незаполненной памяти...
В винде есть нечто типа WaitForSingleObject, но как эта фишка работает, что то не очень понятно...
Я же на писал - по вашему желанию вы можете использовать Synchronize или любой другой способ обеспечения потокобезопасности. Задача - не дать возможность доступа к объекту более чем одного потока одновременно.
Весь вопрос как приостановить выполнение основного кода программы, пока не будет выполнен код потока? Critical Section для этого существует или нет? Что то у меня не получается с ней синхронизировать поток с основным кодом. Если она не для этого, то на кой она вообще нужна?
Немного конкретики:
Я в отдельном потоке запускаю одну функцию (ф-ию поиска устройств). Она довольно ресурсоемкая и чтоб не загонять в ступор всю программу, я пускаю ее отдельным потоком. Но дело в том, что после вызова потока, дальнейший основной код должен работать с памятью, которая будет заполнена в коде потока. Соответственно нужно это дело как то синхронизировать, чтоб не было преждевременного обращения к незаполненной памяти...
В винде есть нечто типа WaitForSingleObject, но как эта фишка работает, что то не очень понятно...
Critical Section необходима для того что бы гарантировать возможность доступа к ресурсу только одного потока в процессе его работы. И до тех пор пока поток работу не закончит - данный ресурс не будет доступен для других в пределах секции естественно.
Функции ожидания (напр WaitForSingleObject) как раз и нужны для ситуаций ожидания освобождения ресурса. Для того чтобы было понятно как она работает - необходимо как минимум внимательно прочесть МСДН. Мне теорию излагать просто в облом - тем более что ради этого мне надо подняться c поломанной ногой к книжному шкафу на второй этаж - но я думаю вас не затруднит ввести в поисковике Рихтер и прочесть что он пишет. С него кстати (и с поиска по форуму) и надо начинать писать многопоточные приложения.
Исходные данные такие: имеется форма, на ней размещены компоненты Memo и кнопка.
Основной код (Unit1.cpp):
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Form1->Memo1->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form1->Memo1->Clear();
Form1->Memo1->Lines->Add("Before thread"); //Выводим текст до создания потока
TestThread *tt=new TestThread(false); //Создаем и запускаем поток
/*
switch(WaitForSingleObject((HANDLE)tt->Handle, 5000))
{
case WAIT_OBJECT_0:
MessageBox(NULL, "Thread successfull", "", MB_OK);
break;
case WAIT_TIMEOUT:
MessageBox(NULL, "Wait Timeout", "", MB_OK);
break;
case WAIT_FAILED:
MessageBox(NULL, "Wait Failed", "", MB_OK);
break;
}
*/
Form1->Memo1->Lines->Add("After thread"); //Дописываем после вызова потока
}
Код потока (Unit2.cpp)
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
__fastcall TestThread::TestThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
this->FreeOnTerminate=true;
}
//---------------------------------------------------------------------------
void __fastcall TestThread::Execute()
{
Synchronize(AddMemo);
/* for(int i=0; i< 1000; i+=10)
{
Beep(i,25);
}
*/
}
//---------------------------------------------------------------------------
void __fastcall TestThread::AddMemo()
{
Form1->Memo1->Lines->Add("In thread");
}
По докам билдера, для доступа к компонентам главного потока VCL, нужно использовать метод Synchronize. Как он точно работает я честно говоря не знаю, но по идее должен приостанавливать главный поток и передавать управление потоку вызвавшему Synchronize.
Как можно увидеть в примере, сообщения выводятся в Memo не в порядке как они указаны в коде. Сначала добавляется запись "Before thread", потом "AfterThread" и уже третьей строкой добавляется "In Thread".
Получается, что вызываемый поток заканчивает свою работу позже, чем основной поток. При этом метод Synchronize не учитывает очередность.
Решил сделать с помощью Win API ожидание WaitForSingleObject. После вызова потока, передаю его хендл API функции ожидания...
Возникает несколько непонятных моментов.
Во первых, если в потоке при этом присутствует метод synchronize, то WaitForSingleObject заканчивается по таймауту. Происходит это видимо потому, что WaitForSingleObject приостанавливает работу главного VCL потока, а функция Synchronize, как раз должна обратиться к главному потоку. Чего не происходит, пока WaitForSingleObject не завершится по таймауту.
Во вторых, если в вызываемом потоке не использовать метод Synchronize, а просто загрузить его каким то долгим по исполнению кодом (см. закомментированый цикл с Beep), то WaitForSingleObject в итоге заканчивается успешно, но во время ожидания, главный поток не отрабатывает системные сообщения, т.е. форму нельзя не передвинуть, ни сделать что либо еще...
Как же сделать так, чтоб форма реагировала на сообщения, но при этом ждала бы завершения работы вызванного потока?
Да, забыл еще один вопрос... Может ли произойти так, что поток завершит работу и освободит свои ресурсы, до того, как его хендл передастся в функцию WaitForSingleObject? Что при этом делать?
Для ожидания окончания работы процесса, используем функцию MsgWaitForMultipleObjects, с помощю которой отлавливаем так же и сообщения, которые потом обрабатываем через PeekMessage, TranslateMessage и DispatchMessage. Все это происходит в бесконечном цикле до момента завершения ожидаемого процесса...
Теперь собственно вопросы, насколько корректно использовать MsgWaitForMultipleObjects в бесконечном цикле ожидания? Не слишком ли это нагрузит процессор? Есть ли какой другой способ чтобы приостановить выполнение основного кода программы, до конца выполнения вызваного потока, но чтобы при этом сохранялась реакция интерфейса?