Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник |
Коллекция курьезных вопросов из форумов
KOL и MCK |
KOL и MCK - Компактные программы на Delphi
Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / rsdn.org

 
Чтобы не потерять эту дискуссию, сделайте закладку « предыдущая ветвь | форум | следующая ветвь »
Страницы: 1 2

Многопоточность. Запись логов


Дмитрий   (14.09.13 19:41

Вроде-бы стандартная ситуация. Создаю файл и пытаюсь в него писать. Из основного модуля все работает. Из потока, действия которого мне тоже надо логгировать - сразу крашится. Как можно решить?

AssignFile(log,GetCurrentDir+'\log.txt');
if FileExists(GetCurrentDir+'\log.txt') then
Reset(Log) else Rewrite(log);

В потоке:
 Writeln(log,TimeToStr(now)+' Connected');


megavoid ©   (14.09.13 19:52[1]

Попахивает битвой за дескриптор :)

Как решить - писать из одного места, оформить процедурой, постараться не юзать паскалевские методы работы с файлами. Где log определена, как именно?


sammy   (14.09.13 19:56[2]

используй  синхронизацию  
http://www.delphi-manual.ru/synchronize.php


Дмитрий   (14.09.13 19:57[3]

глобальный var основной формы. log:text;


sammy   (14.09.13 19:57[4]

код  в процедуре будет выполнен в основном потоке и проблем быть не должно


Дмитрий   (14.09.13 20:00[5]

sammy спасибо, но это надо гонять данные из потока в основной и там уже записывать. Думал обойтись без этого.


Дмитрий   (14.09.13 20:02[6]

Поясню немножко. Поток создает idHTTP и шлет запрос. Ждет ответа и передает его основной форме через Synchronize(MainForm.Update); Мне хотелось-бы скидывать данные из потока в чистом виде до отправки (что посылает) и после отправки (что пришло).  Видимо придется передавать их все основной форме :(


DVM ©   (14.09.13 21:35[7]


> Видимо придется передавать их все основной форме :(

Тебе же написали про синхронизацию доступа к файлу или куда ты там логгируешь. Синхронизация это не только Synchronize и не только с GUI потоком. Выкинь паскалевские файлы, возьми TFileStream и синхронизируй запись в него критической секцией.


sammy   (14.09.13 23:18[8]

в потоке создай такую же переменную, передавай в поток значение log и пиши в него через метод который вызываешь в synchronization, прям в потоке напишешь  
procedure thread.log;
begin
Writeln(log,TimeToStr(now)+' Connected');
end;

procedure thread.execute;
begin
synchronize(log);
end;

p.s. прошу прошения за ошибки, день программиста вчера был :)))


Alaska   (26.10.17 16:26[9]

TLogger=class(TThread)
protected
 CS:TCriticalSection;
 FInterval:integer;
 buff:TStringList;
 LogName:String;
 procedure Execute;override;
public
 constructor Create(FileName:String;MaxCnt:integer;interval:integer);
 destructor  Free;
 procedure   Log(S:String);
 procedure   Flush;
end;

{ TLogger }

constructor TLogger.Create(FileName: String; MaxCnt, interval: integer);
procedure recren(n:string;lvl:integer);
begin
 if (lvl=-1) then
   begin
    if (FileExists(n)and FileExists(n+'.0')) then recren(n,0);
   RenameFile(n,n+'.0');
   end else
 if lvl>MaxCnt then DeleteFile(n+'.'+IntToStr(lvl)) else
  begin
   if FileExists(n+'.'+IntToStr(lvl+1)) then recren(n,lvl+1);
   RenameFile(n+'.'+IntToStr(lvl),n+'.'+IntToStr(lvl+1));
  end;
end;
begin
MaxCnt:=MaxCnt-2;
recren(FileName,-1);
LogName:=FileName;
FInterval:=interval;
buff:=TStringList.Create;
CS:=TCriticalSection.Create;
Inherited Create(FInterval<100);
end;

procedure TLogger.Execute;
begin
 while true do
  begin
    Flush;
    sleep(FInterval);
  end;
end;

procedure TLogger.Flush;
var i,l:integer;T:Text;
begin
 CS.Enter;
 AssignFile(T,LogName);
 if FileExists(LogName) then
 Append(T)
 else
 Rewrite(T);
 l:=buff.Count-1;
 for i:=0 to l do Writeln(T,buff[i]);
 buff.Clear;
 CloseFile(T);
 CS.Leave;
end;

destructor TLogger.Free;
begin
Suspend;
Flush;
CS.Free;
buff.Free;
end;

procedure TLogger.Log(S: String);
begin
 CS.Enter;
 buff.Add(S);
 CS.Leave;
end;


rrrrrrr ©   (26.10.17 16:53[10]

о божечки. как длинно-то .....

unit LogUnit;

interface

procedure Log(const AMsg : string; const AParam : string = '');

implementation

uses Windows, Classes, SysUtils,SyncObjs;

var LogName : string;
   cs : TCriticalSection = nil;

procedure Log(const AMsg : string; const AParam : string = '');
var F:Text;
begin
try
 cs.Enter;
 Assign(F,LogName);
 if FileExists(LogName) then Append(F) else Rewrite(F);
 Writeln(F,FormatDateTime('dd.mm.yyyy hh:mm:ss',Now),#9,GetCurrentThreadID,#9,AMsg,#9,AParam);
finally
 Close(F);
 cs.Leave;
end;
end;

initialization
cs := TCriticalSection.Create;
LogName := ChangeFileExt(Trim(ParamStr(0)),'.log');
finalization
cs.Free;
end.


han_malign ©   (27.10.17 10:36[11]


> о божечки. как длинно-то .....

...
h:= CreateFile(PChar(filePath), {FILE_APPEND_DATA=}$4, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if( h <> INVALID_HANDLE_WALUE )then begin
   WriteFile(h, pointer(str)^, Length(str), dwX, 0);
   CloseHandle(h);
end;

для libc соответсвтенно open(..., O_RDWR or O_CREAT or O_APPEND, ... )/write/close

F:Text - для сценария открытие, запись, закрытие - однозначный криминал - т.к. там промежуточный буфер.(TFileStream - еще хуже, т.к. еще дополнителные операции выделения/освобождения памяти под экземпляр и буфер)

причём для O_APPEND - атомарность однократного write гарантируется документацией, для FILE_APPEND_DATA - явно не документированно, но пока сбоев не было...


rrrrrrr ©   (27.10.17 10:58[12]

да пофик вообще.
модулю этому лет 18-19 , засунут он в папку коммон и трудится он круглые сутки прилинкованый к фик знает скольки процессам созданным за все это время.

был бы там хоть малейший реальный криминал, я бы уже об этом знал


Pavia ©   (27.10.17 15:35[13]


> F:Text - для сценария открытие, запись, закрытие - однозначный
> криминал - т.к. там промежуточный буфер.

Если логов пишется много и выкинуть буфер, то это приведёт к проседанию программы по скорости в разы. Так что без-буфера ну никак нельзя.


> был бы там хоть малейший реальный криминал

Обработка ошибок открытия файла сделана неверно. Лучше TEXT заменить на TFileStream/TMemoryStream


rrrrrrr ©   (27.10.17 15:51[14]

там нет обработки ошибок открытия файла


Игорь Шевченко ©   (27.10.17 20:45[15]


> F:Text - для сценария открытие, запись, закрытие - однозначный
> криминал - т.к. там промежуточный буфер


Чего ?


han_malign ©   (30.10.17 17:51[16]


> Чего ?

function _WriteBytes(var t: TTextRec; const b; cnt : Longint): Pointer;
{$IFDEF PUREPASCAL}
var
 P: PChar;
 RemainingBytes: Longint;
 Temp: Integer;
begin
 Result := @t;
 if t.Mode <> fmOutput and not TryOpenForOutput(t) then Exit;

 P := t.BufPtr + t.BufPos;
 RemainingBytes := t.BufSize - t.BufPos;
 while RemainingBytes <= cnt do
 begin
   Inc(t.BufPos, RemainingBytes);
   Dec(cnt, RemainingBytes);
   Move(B, P^, RemainingBytes);
   Temp := TTextIOFunc(t.InOutFunc)(t);
   if Temp <> 0 then
   begin
     SetInOutRes(Temp);
     Exit;
   end;
   P := t.BufPtr + t.BufPos;
   RemainingBytes := t.BufSize - t.BufPos;
 end;
 Inc(t.BufPos, cnt);
 Move(B, P^, cnt);
end;
{$ELSE}
...



> > F:Text - для сценария открытие, запись, закрытие ...
> Если логов пишется много и выкинуть буфер, то это приведёт к проседанию программы по скорости в разы. Так что без-буфера ну никак нельзя.

 TTextBuf = array[0..127] of Char;
- этот буфер полезен исключительно для форматирования классического типизированного вывода через Write/Writeln...
Потоковый вывод(текстовой информации) и аварийные логи это немного разные задачи...
И мне пока никто убедительно не смог показать, что его буферизация работает лучше файлового кэша Windows, особенно при конкуретной записи в один файл из нескольких потоков(и тем более процессов)...
Буфер даёт весомое увеличение скорости - если выровнен на границу страницы памяти, причём не только по размеру, но и по смещению(и в этом случае небольшой добавочный выигрыш даёт FILE_FLAG_NO_BUFFERING).


Игорь Шевченко ©   (30.10.17 18:13[17]

han_malign ©   (30.10.17 17:51) [16]

У меня собственно вызвала удивление процитированная фраза. Потому что при закрытии этот буфер (и все другие) записывается в файл (в кеш системы, на диск, неважно).


Игорь Шевченко ©   (30.10.17 18:15[18]

Кстати. Сам пользуюсь ведением протокола, аналогично [10], с заменой критической секции на mutex (у меня несколько процессов пишут в один файл), никаких тормозов не наблюдал.


Eraser ©   (30.10.17 23:02[19]

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


Страницы: 1 2 версия для печати

Написать ответ

Ваше имя (регистрация  E-mail 







Разрешается использование тегов форматирования текста:
<b>жирный</b> <i>наклонный</i> <u>подчеркнутый</u>,
а для выделения текста программ, используйте <code> ... </code>
и не забывайте закрывать теги! </b></i></u></code> :)


Наверх

  Рейтинг@Mail.ru     Титульная страница Поиск, карта сайта Написать письмо