Форум: "Основная";
Текущий архив: 2004.08.22;
Скачать: [xml.tar.bz2];
ВнизVCL и многопоточность... Найти похожие ветки
← →
Jolik © (2004-08-08 00:03) [0]Здрассте!
Как народ выкручивается когда надо вызывать визуальные компоненты из неосновного потока программы.
Такая задача:
Из нескольких нитей программы в одну точку приходят текстовые предложения, которые должны выводиться в Мемо. Сейчас я данные из разных нитей сначала группирую в защищенный критическими секциями СтрингЛист, и посылаю, причем через ПостМессаже, сообщение в основное окно. В ответ на это сообщение основное окно добавляет в Мемо данные из СтрингЛиста. Решение подсмотрел в реализации Syncronize Дельфей.
Может есть какой другой способ?
Как то читал, что можно вызвать АПИ функцию которая сообщит когда можно вызывать функции визуальных объектов Виндовс, типа как INT21 в ДОСе. Не знаю, относится ли это к VCL (там ведь и у самих борландов куча кода).
Спасибо!
← →
Fay © (2004-08-08 00:06) [1](Synchronize or SendMessage) and F1
← →
Jolik © (2004-08-08 00:13) [2]Посмотрите исходники Synchronize - убожество. Тем более, что все что я выше написал - тоже самое.
SendMessage - выполняет код обработки сообщений окна В ТОМ ЖЕ ПОТОКЕ откуда вызывается и поэтому не прокатывает. Да и как передать например строку в сообщении? Можно передать только ссылку, и пока сообщение не отработается, ссылка должна быть активной - тоже не очень красиво...
В любом случае, спасибо!
← →
Fay © (2004-08-08 01:08) [3]2 Jolik © (08.08.04 00:13) [2]
>> SendMessage - выполняет код обработки сообщений окна В ТОМ ЖЕ ПОТОКЕ
Это УЖАСНО !!! 8) Проверьте в отладчике.
Сделано нкрасиво - просто показать, что я имел ввиду. Надеюсь, Вы простите мне маленькую лень 8)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
const
WM_FYVM = WM_USER + 345;
type
TForm1 = class(TForm)
Button1 : TButton;
Memo1 : TMemo;
Button2 : TButton;
procedure Button2Click(Sender : TObject);
procedure Button1Click(Sender : TObject);
private
procedure WMFyvm(var M : TMessage); message WM_FYVM;
end;
var
Form1 : TForm1;
implementation
{$R *.dfm}
var
dwEvent : DWORD;
procedure ThreadProc(p : PDWORD); stdcall;
var
r, h : DWORD;
procedure DoMsg(s : string);
begin
SendMessage(Form1.Handle, WM_FYVM, Integer(PChar(@s[1])), 0);
end;
begin
h := p^;
repeat
r := WaitForSingleObject(dwEvent, 1000);
case r of
WAIT_TIMEOUT : DoMsg(Format("Строка от потока %d", [h]));
WAIT_ABANDONED :
begin
DoMsg("Сдохло событие");
Break;
end;
WAIT_OBJECT_0 :
begin
DoMsg(Format("Поток %d прощается с Вами", [h]));
Break;
end;
end;
until False;
CloseHandle(h);
end;
procedure TForm1.Button2Click(Sender : TObject);
begin
PulseEvent(dwEvent);
end;
procedure TForm1.WMFyvm(var M : TMessage);
begin
Memo1.Lines.Add(PChar(M.WParam));
end;
procedure TForm1.Button1Click(Sender : TObject);
var
h, id : DWORD;
begin
h := CreateThread(nil, 0, @ThreadProc, @h, CREATE_SUSPENDED, id);
ResumeThread(h);
end;
initialization
dwEvent := CreateEvent(nil, True, False, nil);
finalization
CloseHandle(dwEvent);
end.
← →
Jolik © (2004-08-08 02:07) [4]Может (и скорее всего) я и не прав - в Syncronize применяется SendMessage, но при использовании SendMessage мои приложения практически моментально зависали... Приходилось менять на PostMessage (Со всеми вытекающими - в момент когда обрабатывается сообщение ссылка на строку может уже быть недействительной...) Попробую еще раз..
Спасибо!
← →
Fay © (2004-08-08 02:15) [5]Да не за что. 8) Удачи.
← →
Мастер © (2004-08-08 02:51) [6]>Jolik
о при использовании SendMessage мои приложения практически моментально зависали...
Вы ведь не привели пример своего кода?
1. При частом использовании SendMessage с визуализацией в основном потоке это естественно. Ведь система только и занимается тем, что выполняет оконную процедуру в Вашей программе.
2. Так же будет и при использовании метода Synchronize(что естественно, ведь метод использует для синхронизации SendMessage.
-----------
Резюме.
При очень частом отображении данных в основном потоке из дополнительного смысл в использовании дополнительного потока теряется.
Т.е. в данном случае нужно менять логику программы, а не пенять на то, что "...Synchronize - убожество..."
← →
Бином Ньютоныч (2004-08-08 08:37) [7]>при использовании SendMessage мои приложения практически моментально зависали
Видимо, из-за неправильного использования критических секций происходит взаимоблокировка.
← →
Fay © (2004-08-08 08:51) [8]2 Бином Ньютоныч (08.08.04 08:37) [7]
Очередь сообщений - невдолбенная критическая секция.
← →
Бином Ньютоныч (2004-08-08 09:11) [9]>Очередь сообщений - невдолбенная критическая секция.
В смысле?
← →
Fay © (2004-08-08 09:57) [10]2 Бином Ньютоныч (08.08.04 09:11) [9]
Посмотрите мой пример. Он, конечно, гуано, но, в целом, понятно.
Я понимаю Ваш вопрос, и знаю, что сравнивать объект ядра с субъектом ядра, по меньшей мере странно, но можно 8).
← →
Бином Ньютоныч (2004-08-08 10:04) [11]>Fay © (08.08.04 09:57) [10]
Вероятно, Вы меня не поняли. Я о том, что причиной зависания(Jolik © (08.08.04 02:07) [4]) мог быть примерно такой псевдокод:
in secondary thread:
...
entercriticalsection(MySection);
somework;
sendmessage(...mymessage, ...);
leavecriticalsection(MySection);
...
in main thread:
procedure MyMessageHandler...
begin
entercriticalsection(MySection);
somework;
leavecriticalsection(MySection);
end;
Deadlock is guaranteed.
← →
k-sergey © (2004-08-08 10:30) [12]я немного не по теме, но раз вы уж заговорили о потоках:
создаб динамически кучу Image и подгружаю в них картинки...естественно прога подвисает, если картинок очень много...
хочеться запихать это все в поток...
может подскажите? :-)
← →
Бином Ньютоныч (2004-08-08 10:40) [13]>k-sergey © (08.08.04 10:30) [12]
Создавайте свою ветку. И четко формулируйте, что именно подсказать.
← →
k-sergey © (2004-08-08 10:53) [14]Ясно.
← →
Sha © (2004-08-08 11:00) [15]> Jolik © (08.08.04 02:07) [4]
> Приходилось менять на PostMessage (Со всеми вытекающими - в
> момент когда обрабатывается сообщение ссылка на строку может
> уже быть недействительной...) Попробую еще раз..
Ссылка при PostMessage всегда будет действительной, если делать примерно так:
//в основном потоке
type
Tfrm=...
procedure FMUpdate(var msg: TMessage); message FM_Update;
...
//не забыть менять Tag и значение визуального объекта (в OnClick и т.п.),
//когда пользователь перемещается по невизуальным объектам
//обработка принятого сообщения об изменении состояния объекта
procedure Tfrm.FMUpdate(var msg: TMessage);
var
Task: TTask; //тот невизуальный объект, к которому относится сообщение
s: string;
begin;
integer(Task):=msg.WParam;
integer(s):=msg.LParam;
Task.Products.Add(s); //обновили навизуальный объект
if integer(Task)=Memo1.Tag then Memo1.Lines.Add(s); //обновили визуальный объект
end;
//в дополнительных потоках - посылка сообщений об изменении
//состояния невизуальных обектов
procedure PostToForm(Msg: cardinal; obj: pointer; const s: string);
var
t: string;
begin;
t:=s; UniqueString(t);
PostMessage(FormHandle,Msg,integer(obj),integer(t));
pointer(t):=nil;
end;
← →
Jolik © (2004-08-08 14:57) [16]Спасибо за отклики - тем более странно что выходной (че девченок мало симпатичных на улице :))))
2Sha: Ни разу не пробывал, но боюсь что
PostMessage(FormHandle,Msg,integer(obj),integer(t));
pointer(t):=nil;
не будет работать...
PostMessage асинхронная функция и к моменту ее отработки t уже не будет... Или код функции PostToForm заставляет Delphi инкрементировать счетчик ссылок на t?
2ALL:
Я не обнаружил особой разницы между непосредственным вызовом Memo1.Lines.Add(S) из потока и вызовом Memo1.Lines.Add(S) из обработчика сообщения при отправке SendMessage. Т.е. приложение так же зависало после некоторого времени работы. Т.о. я сделал вывод что SendMessage это просто вызов (фактически) диспетчера сообщений окна. (В Вынь 3.11 так и было...) В итоге пришлось сделать кэш на TStringList куда все кидается, а раз в секунду в обработчике TTimer уже делается Memo1.Lines.AddStrings(StringList1). Правда информация проскакивает ТАКИМИ КУСКАМИ :)))
Спасибо!
← →
Мастер © (2004-08-08 18:16) [17]>Jolik © (08.08.04 14:57) [16]
Единственное, что тебе нужно добавить в обработчик сообщения, это Application.ProcessMessages.
В этом случае твоя программа не будет "зависать".
← →
Sha © (2004-08-08 20:21) [18]> Jolik © (08.08.04 14:57) [16]
> PostMessage асинхронная функция и к моменту ее отработки t уже не будет...
> Или код функции PostToForm заставляет Delphi инкрементировать счетчик ссылок на t?
Не совсем так. Для того, чтобы сохранить большую свободу и иметь возможность в некоторых процедурах обращаться к данным в string-строках через указатели (т.е. для гарантии неизменности переданных данных) в PostToForm заводится уникальная строка, а в FMUpdate она (по окончании процедуры) уничтожается.
В принципе в приведенном коде вызов UniqueString(t) не обязателен. Можно ее и не вызывать, тогда для не RO-строки PostToForm будет икрементировать счетчик ссылок, а в FMUpdate декрементировать. Однако тогда хакерские штучки при работе с переданной строкой будут противопоказаны.
← →
Jolik © (2004-08-08 21:17) [19]2Sha:
Получается в
//в дополнительных потоках - посылка сообщений об изменении
//состояния невизуальных обектов
procedure PostToForm(Msg: cardinal; obj: pointer; const s: string);
var
t: string;
begin;
t:=s; UniqueString(t);
PostMessage(FormHandle,Msg,integer(obj),integer(t));
pointer(t):=nil;
end;
ошибка (описка) pointer(t):=nil; надо делать в FMUpdate?
← →
Sha © (2004-08-08 21:25) [20]Jolik © (08.08.04 21:17) [19]
Все написано верно. Нет никаких описок :)
← →
Sha © (2004-08-08 21:30) [21]Присваивание pointer(t):=nil; требуется как раз для того, чтобы Delphi не освобождала локальную строку по завершении процедуры.
И, наоборот, в FMUpdate мы не отводим память для локальной строки, но позволяем Delphi освободить ее в финале процедуры.
← →
Chlavik © (2004-08-08 23:56) [22]Я просто делал класс тоже типа TMessagehandler в котором AllocateWND(HandleMessage) ... HandleMessage abstract в наследниках этого класса перекрываеш HandleMessage и в Case Massege.Msg of пишеш реакцию на твои месаги (а их я думаю не так уже и много будет, там MM_SetText, MM_SetCaption и так далее) и пишеш насленик от TThread в котором бдет в protected DoMessage которая будет через SendMessageTimeOut слать на NotifyHandle (он же и будет TMessagehandler.handle) слать месаги и всё афигенно пашет....
← →
Digitman © (2004-08-09 09:20) [23]
> Jolik
одно из изящных решений - передача ссылки на интерфейс IUnknown
IMyData = interface
function Get_Data: String;
procedure Set_Data(const Data: String);
property Data: String read Get_Data write Set_Data;
end;
TMyData = class(TInterfacedObject, IMyData)
private
FData: String;
protected
function Get_Data: String;
procedure Set_Data(const Data: String);
end;
function TMyData.Get_Data: String;
begin
Result := FData;
end;
procedure TMyData.Set_Data(const Data: String);
begin
FData := Data;
end;
..
var
MyData: IMyData;
..
MyData := TMyData.Create;
try
MyData._AddRef;
MyData.Data := "XXXX";
PostMessage(hWnd, WM_XXX, Integer(Pointer(MyData)), 0);
finally
MyData := nil;
end;
...
procedure TMyObject.WMXXX(var Message: TMessage);
var
MyData: IMyData;
begin
MyData := IMyData(Pointer(Message.wParam));
Memo.Lines.Add(MyData.Data);
MyData := nil;
end;
← →
Sha © (2004-08-09 09:40) [24]Фактически [23] - это аналог [15].
Оба варианта - пересылают string наиболее естественным образом.
Первый чуть быстрее, второй понятнее.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2004.08.22;
Скачать: [xml.tar.bz2];
Память: 0.53 MB
Время: 0.038 c