申请说明:
我有一个允许用户通过线程运行多个并发查询的应用程序(一次最多100个)。
我有一个用于记录错误的类。如果应用程序中发生错误,我创建该类的实例并调用过程将错误写入日志文件。
问题:
我需要使错误记录代码线程安全。我注意到如果很多线程同时运行并产生相同的错误(例如无法连接到数据库),我会收到i / o错误32(由于应用程序试图写入一个文件而导致已经开放了。)
作为一个快速而又脏的修复方法,我将写入文件的代码放入try ...除了重复循环内的块之外。如果存在异常(例如,该文件已由该类的另一个实例打开,由另一个线程启动),则它将标志设置为“false”。循环继续执行,直到标志为“true”(即没有错误写入文件),如下所示:
procedure TErrorLogging.logError(error: string);
var
f: textfile;
ok: boolean;
begin
repeat
ok := true;
try
assignfile(f, fLogFilename);
if fileExists(fLogFilename) then append(f) else rewrite(f);
writeln(f, error);
closefile(f);
except
ok := false;
end;
until ok;
end;
我知道保护代码块的正确方法是使用Critical Sections,但我不确定我是如何实现的,因为有许多不同的线程使用了日志类,并且线程的每个实例都有自己的用于写入文件的日志记录类的实例(因此它们不仅仅是针对同一代码块进行同步)。
选项,我可以看到它们:
答案 0 :(得分:6)
每当要添加日志条目时创建日志记录类的实例都是错误的,并且反复打开和关闭日志文件。我个人会使用一个类的一个实例,它在内部使用一个字符串列表,其基本方法是线程安全的。像这样:
type
TErrorLog = class
private
FList: TStringList;
FLock: TRTLCriticalSection;
public
constructor Create;
destructor Destroy; override;
procedure Clear;
procedure Add(const ErrorText: string);
procedure SaveToFile(const FileName: string);
end;
implementation
{ TErrorLog }
constructor TErrorLog.Create;
begin
inherited Create;
InitializeCriticalSection(FLock);
FList := TStringList.Create;
end;
destructor TErrorLog.Destroy;
begin
EnterCriticalSection(FLock);
try
FList.Free;
inherited Destroy;
finally
LeaveCriticalSection(FLock);
DeleteCriticalSection(FLock);
end;
end;
procedure TErrorLog.Clear;
begin
EnterCriticalSection(FLock);
try
FList.Clear;
finally
LeaveCriticalSection(FLock);
end;
end;
procedure TErrorLog.Add(const ErrorText: string);
begin
EnterCriticalSection(FLock);
try
FList.Add(ErrorText);
finally
LeaveCriticalSection(FLock);
end;
end;
procedure TErrorLog.SaveToFile(const FileName: string);
begin
EnterCriticalSection(FLock);
try
FList.SaveToFile(FileName);
finally
LeaveCriticalSection(FLock);
end;
end;
答案 1 :(得分:3)
不知道Delphi,作为一般设计规则(如果可能的话),我会将你的logError函数插入一个线程安全的Array,ArrayList,Queue对象或你已经可用的,然后让它写入文件中背景,也许每隔5-10秒左右。这不仅应该解决I / O问题,还应该扩展到每秒数千次写入,以防您想要记录其他事件以进行调试等。