2

是否有托管的系统级序列号生成器?DateTime.Now.Ticks 不会这样做,因为我正在做的操作有时会在每个滴答声中发生不止一次。


需求说明:

  • 进程不可知论——实际上只有一个进程可以访问它。
  • 性能很关键!这用于记录广告服务器上的展示次数,可以达到 1k/秒

它需要是以下之一:

  • 一个 4 字节的序列号,每个刻度都重置
  • 一个 12 字节的序列号 - 本质上为 DateTime 添加了 4 个字节的粒度
4

7 回答 7

6

没有用于此目的,但您可以使用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;
        }
    }
}
于 2009-04-14T14:13:38.470 回答
1

Guid 与您将获得的一样接近,但它们是“唯一的”并且不一定是连续的。如果您真的想在系统级别跨多个进程按顺序执行,您可能不得不自己动手。

编辑:

好的,所以根据您的新要求,我将假设:

  1. 只需要一个进程来完成这项工作
  2. 您正在追加到数据库

所以这是我推荐的:

  1. 在流程启动时,查询数据库以获取最后(最大)值(如果不存在,则为 0)。
  2. 对每个 DB 行使用简单的 long 和 increment。由于数据速率高,您将希望分批插入。

那应该这样做。把事情简单化。这没有锁,启动时有轻微的(可忽略的)命中,以及数据库中的序列号。只要您只有一个进程运行该算法,它也与进程无关。

于 2009-04-14T14:12:49.830 回答
0

我认为最接近的东西是 guid,我相信你知道它充其量只是部分顺序。

于 2009-04-14T14:12:16.930 回答
0

我认为我们需要更多地了解您在寻找什么。你能澄清一下你的问题吗?尤其是服务...

  • 需要跨所有进程、一个进程或特定用户工作?
  • 数字必须是唯一的还是只是连续的?

根据您的问题,您可能正在寻找几个不同的项目。

需要跨系统上所有进程的一组连续数字

AFAIK,不存在这样的服务。一个应该相当容易编写,但让它在所有进程中工作是很棘手的。

需要跨系统上所有进程的唯一顺序数字组

第一个问题略有不同。这样的服务不存在,因为它不可能实现。使用内置数据类型无法保证唯一的序号,因为该值最终会溢出并给您留下重复的数字。

需要一种在系统中获取唯一值的方法

正如其他几个用户所提到的,最好的选择是 System.Guid 实例。您可以使用 Guid.NewGuid() 创建一个新的。对于几乎所有目的,它们可以被认为是唯一的,但不是连续的。

于 2009-04-14T14:19:34.167 回答
0

这里有一篇文章提供了有关 sql server 的一些详细信息: SQL Server 中的顺序 GUID该技术用于最小化由于 GUID 的随机性而导致的页面拆分。也许这个链接会给你一些提示或想法。

于 2009-04-14T14:24:22.050 回答
0

我喜欢数据库选项只是为了安全。确保你安装一个怪物 SQL 服务器,在你的服务器之间分配带宽,但有足够的内存。在我工作过的第一家公司(甚至在我成为程序员之前)实施了一个类似的系统,它非常狡猾。你可能会努力扩大规模。

另一种选择是在您的代码中实现一个单例函数......只要一个应用程序域将调用它。可能比执行数据库之旅要快一些。但是,如果您无论如何都要把这些东西记录到数据库中......那么将两者结合起来怎么样......运行一个单例以提高速度,然后在资源允许时写入数据库。

同样,如果顺序要求不是那么强,那么 Guid 将是您最好的选择。

于 2009-04-14T15:26:03.600 回答
0

没有锁定就无法获得单个顺序系列。无论您使用什么机制来分配下一个值——性能计数器、静态变量等等——当两个线程同时需要下一个值时,一个线程必须等待另一个线程。

我要做的第一件事是编写一个生成大量线程的测试程序,每个线程重复调用一个锁定增量函数,例如 Daniel Schaffer 发布的那个。这会让你找到你的应用程序开始崩溃的阈值——它花费更多的时间等待而Monitor.Enter不是做任何其他事情。

如果这被证明是一个问题——我敢打赌,如果你谈论的卷是真实的,它会的——那么你应该让每个线程维护自己的顺序计数器,你可以通过标记计数器字段来做到这一点与ThreadStaticAttribute. 然后,您可以根据线程 ID 和计数器的组合生成唯一标识符。

如果您不使用线程池,则此方法将不起作用(因为计数器在它所属的线程执行时死亡)。而且您可能还想让应用程序的启动计数成为复合 ID 的一部分,这样您就不必将线程计数器写入持久存储。(如果您不这样做,当您重新启动服务器时,线程将再次从零开始生成计数器,并且如果您的应用程序创建了一个与早期实例具有相同 ID 的线程,您将获得重复的标识符。)

这显然不是简单的编写(或更重要的是,测试),所以我绝对建议首先证明它是必要的。

于 2009-04-14T19:03:06.403 回答