gpt4 book ai didi

C# 编译器优化循环?

转载 作者:太空宇宙 更新时间:2023-11-03 23:47:40 24 4
gpt4 key购买 nike

我正在尝试控制对对象的访问,以便在给定时间跨度内只能访问特定次数。在我进行的一个单元测试中,访问限制为每秒一次。所以 5 次访问应该只需要 4 秒多一点。然而,测试在我们的 TFS 服务器上失败了,只用了 2 秒。我的代码的精简版本在这里:

public class RateLimitedSessionStrippedDown<T>
{
private readonly int _rateLimit;
private readonly TimeSpan _rateLimitSpan;
private readonly T _instance;
private readonly object _lock;

private DateTime _lastReset;
private DateTime _lastUse;
private int _retrievalsSinceLastReset;

public RateLimitedSessionStrippedDown(int limitAmount, TimeSpan limitSpan, T instance )
{
_rateLimit = limitAmount;
_rateLimitSpan = limitSpan;
_lastUse = DateTime.UtcNow;
_instance = instance;
_lock = new object();
}

private void IncreaseRetrievalCount()
{
_retrievalsSinceLastReset++;
}

public T GetRateLimitedSession()
{
lock (_lock)
{
_lastUse = DateTime.UtcNow;

Block();

IncreaseRetrievalCount();

return _instance;
}
}

private void Block()
{
while (_retrievalsSinceLastReset >= _rateLimit &&
_lastReset.Add(_rateLimitSpan) > DateTime.UtcNow)
{
Thread.Sleep(TimeSpan.FromMilliseconds(10));
}

if (DateTime.UtcNow > _lastReset.Add(_rateLimitSpan))
{
_lastReset = DateTime.UtcNow;
_retrievalsSinceLastReset = 0;
}
}
}

在我的计算机上运行时,在调试和发布中,它都工作正常。但是,一旦我提交给我们的 TFS 构建服务器,我的单元测试就会失败。这是测试:

    [Test]
public void TestRateLimitOnePerSecond_AssertTakesAtLeastNMinusOneSeconds()
{
var rateLimiter = new RateLimitedSessionStrippedDown<object>(1, TimeSpan.FromSeconds(1), new object());

DateTime start = DateTime.UtcNow;

for (int i = 0; i < 5; i++)
{
rateLimiter.GetRateLimitedSession();
}

DateTime end = DateTime.UtcNow;

Assert.GreaterOrEqual(end.Subtract(start), TimeSpan.FromSeconds(4));
}

Test failure from the TFS CI Build Server

我想知道测试中的循环是否以在单独的线程(或类似的东西)上运行循环的每个迭代的方式进行了优化,这意味着测试比它应该完成的更快,因为 Thread.Sleep only阻塞正在调用它的线程?

最佳答案

您的问题出在 Block 方法内部,现在我看了评论,Henk Holterman 似乎已经提出了这个问题。

只有当 _lastReset.Add(_rateLimitSpan)DateTime.UtcNow 相等时才会失败。这不会经常发生,因此它间歇性失败的原因。一个解决方法是在这一行将 > 更改为 >=:

if (DateTime.UtcNow > _lastReset.Add(_rateLimitSpan))

原因不直观,除非您了解每次调用 DateTime.UtcNow 不一定每次调用都返回一个新值。

尽管 DateTime.UtcNow 精确到 100 纳秒,但它的精度与准确度不同。它依赖于机器的定时器间隔,范围为 1-15 毫秒,但通常设置为 15.25 毫秒,除非您正在处理多媒体。

您可以通过这个 dotnetfiddle 看到它的实际效果.除非您打开了一个将计时器设置为不同值(例如 1 毫秒)的程序,否则您会注意到滴答声之间的差异约为 150000 滴答声,大约 15 毫秒,或正常的系统计时器间隔。

我们还可以通过将对 DateTime.UtcNow 的调用提取到临时变量中并在方法结束时比较它们来验证这一点:

    private void Block()
{
var first = DateTime.UtcNow;
while (_retrievalsSinceLastReset >= _rateLimit &&
_lastReset.Add(_rateLimitSpan) > first)
{
Thread.Sleep(TimeSpan.FromMilliseconds(10));
first = DateTime.UtcNow;
}

var second = DateTime.UtcNow;
if (second > _lastReset.Add(_rateLimitSpan))
{
_lastReset = DateTime.UtcNow;
_retrievalsSinceLastReset = 0;
}

if (first == second)
{
Console.WriteLine("DateTime.UtcNow returned same value");
}
}

在我的机器上,所有对 Block 的调用都打印出 DateTime.UtcNow 是相等的。

关于C# 编译器优化循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27071516/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com