multithreading - Creating/Using FileStream Thread Safe -


in application when write text files (logs, traces, etc), use tfilestream class. there cases write data in multithreaded environment, steps:

1- write cache data
2- each 1000 lines save file.
3- clear data.

this process repeated during processing.

problem description:

with 16 threads, system throws following exception:

access violation - file in use application.
guess happening because handle used 1 thread not closed yet, when thread needs open.

i changed architecture following: (bellow new implementation)
in previous way, tfilestream created filename , mode parameters, , destroyed closing handle (i wasn't using tmyfilestream)

tmyfilestream = class(tfilestream) public    destructor destroy; override; end;  tlog = class(tstringlist) private   ffilehandle: integer;   firsttime: boolean;   fname: string; protected   procedure flush;   constructor create;   destructor destroy; end;    destructor tmyfilestream.destroy; begin   //do not close handle, yet!   fhandle := -1;   inherited destroy; end;  procedure tlog.flush; var   strbuf: pchar; logfile: string;   f: tfilestream;   internalhandle: cardinal; begin   if (text <> '')   begin     logfile:= getdir() + fname + '.txt';     forcedirectories(extractfilepath(logfile));     if ffilehandle < 0     begin       if firsttime         firsttime := false;        if fileexists(logfile)         if not sysutils.deletefile(logfile)           raiselastoserror;        internalhandle := createfile(pchar(logfile), generic_read or generic_write,         file_share_read, nil, create_new, 0,0);       if internalhandle = invalid_handle_value         raiselastoserror       else if getlasterror = error_already_exists       begin         internalhandle := createfile(pchar(logfile), generic_read   or generic_write, file_share_read, nil, open_existing, 0,0);         if internalhandle = invalid_handle_value           raiselastoserror         else           ffilehandle := internalhandle;       end       else         ffilehandle := internalhandle;     end;      f := tmyfilestream.create(ffilehandle);     try       strbuf := pchar(text);       f.position := f.size;       f.write(strbuf^, strlen(strbuf));           f.free();     end;      clear;   end; end;  destructor tlog.destroy; begin   fuserlist:= nil;   flush;   if ffilehandle >= 0     closehandle(ffilehandle);   inherited; end;  constructor tlog.create; begin   inherited;         firsttime := true;         ffilehandle := -1; end; 

there better way?
implementation correct?
may improve this?
guess handle right?

all theads use same log object.

there no reentrance, checked! there wrong tfilestream.

the access add synchronized, mean, used critical session, , when reaches 1000 lines, flush procedure called.

p.s: not want third-party component, want create own.

well, start, there's no point in tmyfilestream. looking thandlestream. class allows supply file handle lifetime control. , if use thandlestream you'll able avoid rather nasty hacks of variant. said, why bothering stream? replace code creates , uses stream call setfilepointer seek end of file, , call writefile write content.

however, using that, proposed solution requires further synchronization. single windows file handle cannot used concurrently multiple threads without synchronisation. hint in comment (should in question) serializing file writes. if fine.


Comments

Popular posts from this blog

jquery - How can I dynamically add a browser tab? -

node.js - Getting the socket id,user id pair of a logged in user(s) -

keyboard - C++ GetAsyncKeyState alternative -