개체에 대한 액세스를 제어하여 주어진 timespan 동안 특정 횟수에만 액세스 할 수 있습니다. 내가 가지고있는 단위 테스트에서 액세스는 초당 한 번으로 제한됩니다. 따라서 5 회의 액세스는 4 초 이상 걸릴 것입니다. 그러나 TFS 서버에서는 2 초 만에 테스트가 실패합니다. 이 작업을 수행하려면 코드의 버전을 박탈은 여기에 있습니다 :C# 컴파일러 최적화 루프?
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));
}
하는 Thread.Sleep은 호출되는 스레드 만 차단하기 때문에 테스트가 더 빨리 완료되어야 함을 의미합니다.
코드에 이상한 점이 있습니다 (예 :'_lastUse'가있는 이유를 알 수 없으며'DateTime.UtcNow' 대신'Stopwatch'를 사용하여 시간을 추적하는 것이 더 낫습니다) 그러나 나는 그 문제를 설명 할 명백한 어떤 것도 잘못 보이지 않습니다. 유일한 이유는 ('Stopwatch'를 사용하지 않으므로) 어떤 이유로 시스템 시간이 업데이트되지 않으면 시계 시간이 실제 경과 시간을 잘못 측정한다는 것입니다. 이것은 대개 컴파일러 최적화, 특히 스레딩과 관련이 있습니다. –
'_lastReset.Add (_rateLimitSpan) == DateTime.UtcNow' 때 어떤 일이 일어나는지 스스로에게 물어보십시오. 또한, 잘 작성된이 코드의 경우'_lastReset = default (DateTime)'초기화에 의존하는 것이 궁금하고 불투명하다. 또한 실제 코드는'Sleep()'해서는 안됩니다.Waithandle 등을 기다리는 것을 고려하십시오. –
이론적으로는 이론적으로 가능한이 테스트가 실패 할 수있는 방법은 테스트를 실행하는 동안 시간 서버와의 동기화가 이루어지고 (그리고 제어 할 수없는 경우) 비록 이것이 실제로 문제가된다면 그것은 나에게 매우 놀랄 것입니다. – hvd