![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Ведение лога в многопоточном приложении
Cooller © (2007-02-20 22:52:00)Есть серверное приложение, активно работающая одновременно с несколькими клиентами. Некоторые события необходимо выводить в лог (как в файл, так и в RichEdit).
Вот сама процедура:
procedure ATL(msg:string; showmsg:boolean=false);
begin
try
Append(F);
WriteLn(F,DateTimeToStr(now)+': '+msg);
CloseFile(F);
except
end;
end;
FormCreate:
AssignFile(F,opt.curlogname);
{$I-}
Append(F);
{$I+}
If IOResult<>0 then Rewrite(F);
CloseFile(F);
В RichEdit добавляю просто с помощью Lines.Add
Так вот, несмотря на то, что у меня во всех процедурах и функциях стоят оработчики try except end, с выводом сообщения об ошибке в лог, периодически получаю 'Access Violation'. Может быть проблема и не в добавлении в лог, но сейчас пересматриваю весь код и возникла мысль, что коллизии могут возникать и в этом месте.
Подскажите как правильно организовать вывод информации?
Суслик (2007-02-21 16:33:00) [21]2Coller
Для того, чтобы сильно продвинуться в многопоточности нужно:
1. что-то почитать
2. обязательно смотреть код.
мне в свое время очень помогло изучение *кода* класса TMultiReadExclusiveWriteSynchronizer.
Очень ставит мозги. Я не все понял, конечно, но полезно однозначто было.
Cooller (2007-02-21 16:24:00) [22]2 Суслик
Спасибо за советы. Буду разбираться.
А что не все так просто я понял когда еще несколько лет назад знакомился с ASM и необходимо было подменять вектора прерываний. Правда тогда все решалось использованием cli и sti =)
Суслик (2007-02-21 15:59:00) [23]
> [12] Leonid Troyanovsky © (21.02.07 12:58)
> А зачем в форме? Richedit вполне понимает EM_*.
> Которые ему SendMessage, конечно.
Согласен. Но мой вариант имхо более гобок, т.к. перед добавлением может понадобиться доп. обработка.
К тому же я winapi плохо знаю, чтобы советовать события контролам кидать :)
Суслик (2007-02-21 15:55:00) [24]Поставь себе средство какое-нибудь, чтобы стек вызовов при исключении показывало. Можешь в jedi покопаться - я иногда этим пользуюсь.
----------
Общее правило таково. Многопоточное программирование сильно отличается от однопоточного в части философии. В таком программировании могут быть всякие разные неожиданные эффекты. Не всегда доступ из нескольких потоков БЕЗ синхронизации запрещен. Например ты спокойно можешь обращаться и менать глоб. булеву переменную без всякой синхронизации. Если не ошибаюсь, то и integer можешь. А вот int64 уже нет, ибо она делается двумя машинными командами.
Это я к тому, что многопоточное программирование просто так снаскока не возьмешь. Нужно читать, изучать чужой код (да хоть тот же TThread и прочие многопоточные вещи из rtl и vcl). Можешь Рихтера почитать - программирование в win32. У него много про межпоточнкую синхронизацию - мозги точно разовьешь.
-----
Я понимаю, что у тебя конкретная проблема. Но и решение ее должно быть кокретно. Т.е. что-либо внятное дистанционно сказать сложно.
-----
Приведенный выше код с крит. секциями не очень хорош лучше так
unit LogSupport;
interface
procedure Log(const AMsg:string; const AParam:string = '');
implementation
uses Classes, SysUtils, SyncObjs;
var LogName:string;
cs : TCriticalSection = nil;
procedure Log(const AMsg : string; const AParam : string = '');
var
F:Text;
begin
cs.Enter;
try
Assign(F,LogName);
if FileExists(LogName) then Append(F) else Rewrite(F);
try
Writeln(F,FormatDateTime('dd.mm.yyyy hh:mm:ss',Now),#9,AMsg,#9,AParam);
finally
Close(F);
end;
finally
cs.Leave;
end;
end;
initialization
cs := TCriticalSection.Create;
LogName:=ChangeFileExt(ParamStr(0),'.log');
finalization
cs.Free;
end.
Cooller (2007-02-21 15:41:00) [25]Просто программа хоть и не большая, но кода все-таки на 1500 строк. Вроде бы закрыл все "критические" места, но все равно получаю Access Violation. Даже и не знаю как выловить причину.
Cooller (2007-02-21 15:34:00) [26]Вообще какие бы могли дать общие советы? Использовать как можно меньше глобальный переменных? В некоторых случаях они необходимы, получается потоки должны работать с ними только с синхронизацией? Или в общем случае она не нужна, а есть только некоторые исключения?
Reindeer Moss Eater (2007-02-21 15:06:00) [27]то не "жирно" ли каждый раз вызывать FileExists? =)
А там прикинь, еще каждый раз с объектом синхронизации телодвижения вставлены. Вообще кошмар.
Суслик (2007-02-21 15:04:00) [28]проведи анализ - используй QueryPerformanceFrequency и QueryPerformanceCounter и пойми, сколько времени занимает вызов FileExists в общем времени работы алгоритма.
Cooller (2007-02-21 15:01:00) [29]2 Reindeer Moss Eater
Может дурацкий вопрос, но у меня почему-то на эту тему постоянная паранойя (после некоторых лекций в универе). Если Log, к примеру, вызывается по 5 раз в секунду, то не "жирно" ли каждый раз вызывать FileExists? =)
Reindeer Moss Eater (2007-02-21 13:05:00) [30]unit LogSupport;
interface
procedure Log(const AMsg:string; const AParam:string = '');
implementation
uses Classes, SysUtils, SyncObjs;
var LogName:string;
cs : TCriticalSection = nil;
procedure Log(const AMsg : string; const AParam : string = '');
var F:Text;
begin
cs.Enter;
Assign(F,LogName);
try
if FileExists(LogName) then Append(F) else Rewrite(F);
Writeln(F,FormatDateTime('dd.mm.yyyy hh:mm:ss',Now),#9,AMsg,#9,AParam);
finally
Close(F);
cs.Leave;
end;
end;
initialization
cs := TCriticalSection.Create;
LogName:=ChangeFileExt(ParamStr(0),'.log');
finalization
cs.Free;
end.
Leonid Troyanovsky (2007-02-21 12:58:00) [31]
> Суслик © (21.02.07 11:57) [11]
> 2 При доступе к richedit используй postmessage. событие
> лови в форме.
А зачем в форме? Richedit вполне понимает EM_*.
Которые ему SendMessage, конечно.
--
Regards, LVT.
Суслик (2007-02-21 11:57:00) [32]1 Используй критические секции при доступе к файлу.
2 При доступе к richedit используй postmessage. событие лови в форме. только договорись о том, как данные (строки) передавать.
Хинт (2007-02-21 10:36:00) [33]
> Ну можно, только нужно хорошо понимать, что делаешь. Только
> вопрос - зачем.
В [2] я описал ситуацию. Synchronize это ведь метод TThread...
DrPass (2007-02-21 10:30:00) [34]
> вместо synchronize можно использовать sendmessage?
Можно и так. Или PostMessage. С точки зрения производительности - более эффективный вариант
Суслик (2007-02-21 10:27:00) [35]Ну можно, только нужно хорошо понимать, что делаешь.
Только вопрос - зачем.
synchronize реализован как раз на одном из методов (postmessage or sendmessage - зависит от версии дельфи).
причем они это сделали очень неплохо - весьма продуманный код.
я бы тебе не советовал делать такую синхронизацию самому.
Cooller (2007-02-21 09:57:00) [36]2 Суслик
Спасибо за ответ. Хотел спросить еще одну вещь... вместо synchronize можно использовать sendmessage?
Суслик (2007-02-21 02:17:00) [37]
> Cooller (20.02.07 23:51) [5]
ну ситуация такова.
vcl не является многопоточной. если ты хочешь вызвать метод из библиотеки vcl (читай из визуального компонента), то ты обязан это делать в контексте основного потока. для того, чтобы выполнить код из доп. потока в контексте основного потока тебе нужно использовать метод synchronize из класса tthread.
вообще использование vcl из разных потоков без синхронизации не обязательно влечет av. но делать этого все же нельзя - ибо vcl не спроектирована как многопоточная.
вполне возможно, что у тебя есть некоторые другие ошибки в программе, приводящие к av.
для синхронизации общих данных используй критические секции.
Cooller (2007-02-20 23:51:00) [38]В том и дело, что ошибки другого рода, которые проявляются через Access Violation. Для того, чтобы понять как их устранить - я и создал тему.
Cooller (2007-02-20 23:50:00) [39]
> Выкинь отовсюду try except end и начни исправлять свои
> баги.
Если бы они были, то конструкция хотя бы иногда срабатывала
Loginov Dmitry (2007-02-20 23:25:00) [40]> Так вот, несмотря на то, что у меня во всех процедурах и
> функциях стоят оработчики try except end, с выводом сообщения
> об ошибке в лог, периодически получаю 'Access Violation'.
Выкинь отовсюду try except end и начни исправлять свои баги.