直到现在,每当我想创建一个文件而不覆盖现有文件时,我就做过这样的事情:
if not FileExists(filename) then
stream := TFileStream.Create(filename, fmCreate);
但这不是线程安全的。所以现在我正在寻找一个线程安全版本。
也许我可以组合一些模式,以便TFileStream.Create(filename, fmCreate +fm???);
如果文件存在则失败?
我需要这个与旧DOS程序通信目录锁。但是DOS程序没有打开文件。 : - (
答案 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
中使用它,则尝试创建该文件的第二个线程将失败。