如何创建线程保存文件而不覆盖现有文件?

时间:2011-05-30 10:15:57

标签: delphi thread-safety

直到现在,每当我想创建一个文件而不覆盖现有文件时,我就做过这样的事情:

if not FileExists(filename) then
  stream := TFileStream.Create(filename, fmCreate);

但这不是线程安全的。所以现在我正在寻找一个线程安全版本。

也许我可以组合一些模式,以便TFileStream.Create(filename, fmCreate +fm???);如果文件存在则失败?

我需要这个与旧DOS程序通信目录锁。但是DOS程序没有打开文件。 : - (

2 个答案:

答案 0 :(得分:7)

TFileStream基于文件名的构造函数依赖于WIndows API调用CreateFile来创建将用于访问文件的文件句柄。 API本身有多个参数,特别感兴趣的是创建处置:如果指定CREATE_NEW,如果文件已经存在,则函数将失败。您可以通过自己调用CreateFile,然后使用返回的句柄创建TFileStream来利用它。你可以这样做,因为TFileStream继承自THandleStream,继承了它的基于句柄的构造函数并拥有句柄(在传递给构造函数的句柄上调用CloseHandle)。

由于这依赖于OS提供的函数CreateFile,因此它将是trehad-safe(FileExists()之间没有竞争条件并且实际创建文件。它还阻止旧应用程序访问新的文件,直到你真正关闭手柄。

var FH: NativeUInt;

// ...

  FH := CreateFile('FileName', GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_NEW, 0, 0);
  if FH = INVALID_HANDLE_VALUE then
  begin
    // Creating the new file failed! I'm raizing an exception, but you can do something
    // better-suited for your case, like trying a new file name.
    RaiseLastOSError;
  end;
  // Create the stream using the prepared Handle
  HS := TFileStram.Create(FH);

答案 1 :(得分:4)

我会保留FileExists检查,因为它处理大多数情况而不依赖于异常处理。对于边界情况,您必须正确处理TFileStream构造函数中的异常。如果您在共享模式fmShareDenyWrite中使用它,则尝试创建该文件的第二个线程将失败。