Конференция "WinAPI" » Многопоточность. Запись логов
 
  • Дмитрий (14.09.13 19:41) [0]
    Вроде-бы стандартная ситуация. Создаю файл и пытаюсь в него писать. Из основного модуля все работает. Из потока, действия которого мне тоже надо логгировать - сразу крашится. Как можно решить?

    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]
    смотря какой объем нужно логировать. там, где большой поток данных лучше использовать отдельный поток для сброса данных на диск. так как из-за антивирусов могут появится огромные просадки в процедуре логирования, сталкивался с таким.
 
Конференция "WinAPI" » Многопоточность. Запись логов
Есть новые Нет новых   [88911   +5][b:0.001][p:0.004]