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
Post a Comment