是否有托管的系统级序列号生成器?DateTime.Now.Ticks 不会这样做,因为我正在做的操作有时会在每个滴答声中发生不止一次。
需求说明:
- 进程不可知论——实际上只有一个进程可以访问它。
- 性能很关键!这用于记录广告服务器上的展示次数,可以达到 1k/秒
它需要是以下之一:
- 一个 4 字节的序列号,每个刻度都重置
- 一个 12 字节的序列号 - 本质上为 DateTime 添加了 4 个字节的粒度
是否有托管的系统级序列号生成器?DateTime.Now.Ticks 不会这样做,因为我正在做的操作有时会在每个滴答声中发生不止一次。
需求说明:
它需要是以下之一:
没有用于此目的,但您可以使用System.Diagnostics.PerformanceCounter。您也可以使用注册表,但您需要跨进程序列化读/写访问。
System.Diagnostics.PerformanceCounter pc
= new System.Diagnostics.PerformanceCounter("SeqCounter", "SeqInstance");
long myVal=pc.Increment();
我想得越多,我认为这可能是一个很好的解决方案。增量将通过原子操作将计数器增加 1,该原子操作应该在系统上的所有进程中工作。
根据您的编辑,我不建议使用性能计数器。性能计数器是一种跨多个进程协调的方法。我不确定内部实现是如何编码的。
为什么你不能只使用一个静态变量并增加它?如果您希望它是线程安全的,您将不得不锁定某些东西。
System.Threading.Interlocked.Increment
仅供参考:如果您在 32 位系统上使用长版本,它不一定是线程安全的。
编辑以显示我使用的实现(DS):
public static class Int32Sequencer
{
private static Int32 lastSequence = Int32.MinValue;
private static Object lockObject = new Object();
public static Int32 GetNextSequence()
{
lock (lockObject)
{
unchecked { lastSequence++; }
return lastSequence;
}
}
}
Guid 与您将获得的一样接近,但它们是“唯一的”并且不一定是连续的。如果您真的想在系统级别跨多个进程按顺序执行,您可能不得不自己动手。
编辑:
好的,所以根据您的新要求,我将假设:
所以这是我推荐的:
那应该这样做。把事情简单化。这没有锁,启动时有轻微的(可忽略的)命中,以及数据库中的序列号。只要您只有一个进程运行该算法,它也与进程无关。
我认为最接近的东西是 guid,我相信你知道它充其量只是部分顺序。
我认为我们需要更多地了解您在寻找什么。你能澄清一下你的问题吗?尤其是服务...
根据您的问题,您可能正在寻找几个不同的项目。
AFAIK,不存在这样的服务。一个应该相当容易编写,但让它在所有进程中工作是很棘手的。
第一个问题略有不同。这样的服务不存在,因为它不可能实现。使用内置数据类型无法保证唯一的序号,因为该值最终会溢出并给您留下重复的数字。
正如其他几个用户所提到的,最好的选择是 System.Guid 实例。您可以使用 Guid.NewGuid() 创建一个新的。对于几乎所有目的,它们可以被认为是唯一的,但不是连续的。
这里有一篇文章提供了有关 sql server 的一些详细信息: SQL Server 中的顺序 GUID该技术用于最小化由于 GUID 的随机性而导致的页面拆分。也许这个链接会给你一些提示或想法。
我喜欢数据库选项只是为了安全。确保你安装一个怪物 SQL 服务器,在你的服务器之间分配带宽,但有足够的内存。在我工作过的第一家公司(甚至在我成为程序员之前)实施了一个类似的系统,它非常狡猾。你可能会努力扩大规模。
另一种选择是在您的代码中实现一个单例函数......只要一个应用程序域将调用它。可能比执行数据库之旅要快一些。但是,如果您无论如何都要把这些东西记录到数据库中......那么将两者结合起来怎么样......运行一个单例以提高速度,然后在资源允许时写入数据库。
同样,如果顺序要求不是那么强,那么 Guid 将是您最好的选择。
没有锁定就无法获得单个顺序系列。无论您使用什么机制来分配下一个值——性能计数器、静态变量等等——当两个线程同时需要下一个值时,一个线程必须等待另一个线程。
我要做的第一件事是编写一个生成大量线程的测试程序,每个线程重复调用一个锁定增量函数,例如 Daniel Schaffer 发布的那个。这会让你找到你的应用程序开始崩溃的阈值——它花费更多的时间等待而Monitor.Enter
不是做任何其他事情。
如果这被证明是一个问题——我敢打赌,如果你谈论的卷是真实的,它会的——那么你应该让每个线程维护自己的顺序计数器,你可以通过标记计数器字段来做到这一点与ThreadStaticAttribute
. 然后,您可以根据线程 ID 和计数器的组合生成唯一标识符。
如果您不使用线程池,则此方法将不起作用(因为计数器在它所属的线程执行时死亡)。而且您可能还想让应用程序的启动计数成为复合 ID 的一部分,这样您就不必将线程计数器写入持久存储。(如果您不这样做,当您重新启动服务器时,线程将再次从零开始生成计数器,并且如果您的应用程序创建了一个与早期实例具有相同 ID 的线程,您将获得重复的标识符。)
这显然不是简单的编写(或更重要的是,测试),所以我绝对建议首先证明它是必要的。