多线程文件通过现有对象写入

时间:2013-03-26 17:53:49

标签: multithreading delphi

申请说明:

我有一个允许用户通过线程运行多个并发查询的应用程序(一次最多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,但我不确定我是如何实现的,因为有许多不同的线程使用了日志类,并且线程的每个实例都有自己的用于写入文件的日志记录类的实例(因此它们不仅仅是针对同一代码块进行同步)。

选项,我可以看到它们:

  1. 使用上面的代码。保留此代码是否有任何问题?这是一个快速而肮脏的修复,但它确实有效。
  2. 使用全局TCriticalSection(如何?)。
  3. 在某个地方使用单个过程创建一个日志记录类的实例,线程将与之同步(我猜想这会破坏拥有日志记录类的对象)。

2 个答案:

答案 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问题,还应该扩展到每秒数千次写入,以防您想要记录其他事件以进行调试等。