如何确保跨托管/非托管边界严格排序事件

时间:2017-10-17 18:47:09

标签: c# c++

我有C#代码,它生成带时间戳的事件。它们还获得序列号,因此可以严格排序(甚至跨线程)。这些方面的东西:

private static int NextInSequence = 0;
private int Sequence;
private DateTime TimeStamp;
private void CaptureState()
{
    this.TimeStamp = DateTime.UtcNow;
    this.Sequence = Interlocked.Increment(ref NextInSequence);
}

理想情况下,只需要时间戳将它们按正确的顺序排列,但它可以在DateTime.UtcNow的单个增量的分辨率内生成许多事件。所以当时间戳相等时,Sequence用于排序。

在我将C ++引入混音之前,这些都不是问题。我将代码移植到C ++,并且该代码是从C#代码PInvoked,因此来自两者的事件混合在一起。他们以相同的方式计算时间戳,但我发现当C ++代码生成与C#代码具有相同时间戳的事件时,我遇到了问题。所有C ++的东西都是有序的,C#也是有序的,但它们的交互是乱序的。

因此,如果在线程上以实际顺序发生事件:

Managed event => Unmanaged event => Unmanaged event => Managed event

它可能会像这样排序:

Managed event => Managed event => Unmanaged event => Unmanaged event

线程之间的交互顺序并不重要,因为确保在一个线程上发生的所有事情都保持与创建它的顺序相同。

我想如果我使用更高分辨率的时间戳,那么每个事件都会获得一个独特的时间戳,我就会开展业务。所以下一次尝试使用QueryPerformanceCounter,类似这样(不包括错误检查等):

static double tickFrequency = 10000000.0 / System.Diagnostics.Stopwatch.Frequency;
static long UtcToPCDiff = CalcUtcDiff();
private static CalcUtcDiff()
{
    return DateTime.Utc.ToBinary() - (long)(Stopwatch.GetTimestamp() * tickFrequency);
}
public static GetUtcNow()
{
    return DateTime.FromBinary(UtcToPCDiff + (long)(Stopwatch.GetTimestamp() * tickFrequency));
}

这适用于有限的测试。所有事件都按顺序出现,相对时间戳精确到完整的DateTime精度。但后来我读了许多关于QueryPerformanceCounter不可靠的评论。我意识到,如果PC进入休眠状态然后恢复,或CPU改变频率等,我的时间戳将完全不准确。

我考虑过并被解雇的事情:

  • 我可以通过参考C ++代码传递C#“NextInSequence”并让它使用该变量。但这取决于静态变量的地址永远不会改变。现在可能是这样,但不保证将来会保持这种状态。
  • 我可以使用Marshal来创建一个指向非托管内存的指针,我知道它会保持不变,但是我不能在IntPtr指向的内存上调用Interlocked.Increment而没有一些反射shenanigans。
  • 我可以固定一个1 int的数组,但长期固定可能会使垃圾收集器生活困难(尽管忽略GC问题,这可能是最干净的解决方案)。
  • 我可以从C ++到C#代码进行回调,以请求下一个序列。但这会带来相当大的性能影响。
  • 我可以调用GetSystemTimePreciseAsFileTime,看起来它完全符合我的需要。但是在Windows 8之前该功能不存在,而且必须与Windows XP一起使用。

但我喜欢定义序列,但时间戳必须至少接近UtcNow。

任何人对如何订购活动都有更好的想法?

0 个答案:

没有答案