我能否基于进程ID和线程ID创建一个唯一的文件名?

3
我有一个Delphi(Win32)Web应用程序,可以作为CGI应用程序、ISAPI或Apache DLL运行。我想生成一个唯一的文件名前缀(对于给定时刻的所有当前请求都是唯一的),并且认为最好的方法是使用ProcessID(处理CGI模式)以及threadID(处理DLL模式)。
如何在Delphi中获取唯一的Process ID和Thread ID?
在多核/多处理器情况下(在单个Web服务器机器上),它们是否会是唯一的?
请注意,我被建议不要采用这种方法,因此接受的答案使用了不同的方法来生成临时文件名。
7个回答

5

你提出了很多好的想法。

它是否也会创建一个空文件以“锁定”名称?

不会;我相信我们依赖于Windows来确保同一台计算机自启动以来永远不会再次使用相同的临时文件名。

如果在生成名称和创建文件之间有分秒延迟(如果我需要自己创建文件),是否有任何冲突的可能性?

没有,那将是非常糟糕的事情。

这是我一直在使用的获取临时文件的程序。

function GetTemporaryFileName:string;
var
  Path, FileName: array[0..MAX_PATH] of Char;
begin
  Win32Check(GetTempPath(MAX_PATH, Path) <> 0);
  Win32Check(GetTempFileName(Path, '~EX', 0, FileName) <> 0);
  Result:=String(Filename);
end;

你可以使用JCL中的JclFileUtils.pas文件中的FileGetTempName()函数代替。

我选择了这种方法,因为它最简单,并且从将临时文件放入系统临时文件夹的角度来看更加“正确”。我原本认为我的进程+线程ID的想法可行,但我还需要添加代码来处理一个线程创建多个文件的情况。 - Graza

5

Windows提供了创建保证唯一文件名的功能。无需自己创建:

这里是一个Delphi封装,围绕这个功能:

function CreateTempFileName(aPrefix: string): string;
var
  Buf: array[0..MAX_PATH] of Char;
  Temp: array[0..MAX_PATH] of Char;
begin
  GetTempPath(MAX_PATH, Buf);
  if GetTempFilename(Buf, PChar(aPrefix), 0, Temp) = 0 then
  begin
    raise Exception.CreateFmt(sWin32Error, [GetLastError, SysErrorMessage(GetLastError)]);
  end;
  Result := string(Temp);
end;

我会选择这个。请注意,MSDN文档中提到:“由于用于生成文件名的算法,在创建具有相同前缀的大量文件时,GetTempFileName的性能可能较差。在这种情况下,建议您根据GUID构造唯一的文件名。” - Jamie

4

你能不能使用全局唯一标识符(GUID)代替呢?

注:第一次应该说,查看以下两个函数:

CreateGuid
GuidToString

3

在 Windows 上,进程 ID 不能保证唯一性。它们在进程的生命周期内肯定是唯一的,但一旦进程终止,其 ID 可能会立即被重用。我不确定线程 ID 是否也是如此。如果这些是临时文件,您可以使用类似于 tmpfile 或 tmpnam 的东西(C 函数,但我假设 Delphi 有等效函数)。

正如 Jamie 所发表的,GUID 可能更好。


在给定的时刻,对于所有当前请求都是唯一的 - 重用进程ID是可以的,因为文件只存在于请求的生命周期内。但是,我想确保在多核系统上,在给定的时刻它们将是唯一的。并且添加线程ID将考虑到多线程。 - Graza
这意味着文件是临时的(至少看起来是这样),在这种情况下,我会使用tmpfile或其Delphic等效物。 - grieve

3

1)如何在Delphi中获取唯一的进程ID和线程ID:

答案:
注意:确保在实现部分的uses子句中添加“windows”
注意:Cardinals是无符号32位整数,范围从0到4294967295

implementation
uses Windows;

procedure MySolution();
var
  myThreadID:Cardinal;  
  myProcessID:Cardinal;
begin
  myThreadID := windows.GetCurrentThreadID;
  myProcessID := windows.GetCurrentProcessId;
end;

2) 在多核/多处理器情况下(在单个Web服务器机器上),这些标识符是否唯一?
答案:是。

进程标识符从创建进程开始到进程被终止期间有效,且在整个系统中是唯一的(不仅仅是唯一于处理器)。

线程标识符在线程终止之前唯一地识别整个系统中的线程。(同样,它在整个系统中是唯一的,而不是唯一于处理器)


2
比起这两个选项,你应该使用系统函数_tempnam。它会返回一个不存在的文件的目录下的随机文件名。如果你想要,你可以给_tempnam提供一个前缀,以便创建的文件是可识别为你自己的。如果提供了唯一的前缀,就不用担心有人会打开你的文件。然而,还有另一种解决方案。
_tempnam只适用于将文件放入任意目录中。如果你不在意目录是否为系统临时目录,请改用tempfile_s。它也会为你创建文件,因此不必担心竞争条件......只有当尝试打开超过系统处理能力的临时文件时才会出现错误。对于tempfile_s的最大问题在于,在你关闭文件后,文件将会消失。
编辑:我得到了一个负评,因为这是一个C函数。您可以通过导入它们到Delphi来访问C运行时库。在此处查看一些使用msvcrt.dll的示例。
function _tempnam(const Dir: PChar, const Prefix: PChar): PChar; cdecl;
  external 'msvcrt.dll' name '_tempnam';

它是否也会创建一个空文件来“锁定”名称?也就是说,在生成名称和创建文件之间存在一小段时间延迟(如果我需要自己创建文件),是否有任何冲突的可能性? - Graza
不确定Delphi是否默认提供此功能。GetTempFileName的MSDN文档确实提到了使用GUID。 - Jamie

1

其他人都给了你很好和合理的建议,但是 - 如果你正在使用文件进行临时存储,并且如果这些文件将始终首先创建(如果磁盘上已经有一个同名的剩余文件也无所谓,因为你肯定会覆盖它),那么进程ID_线程ID方法是完全有效的。

使用 GetCurrentProcessIDGetCurrentThreadID Win32 调用来访问这两个 ID。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接