在这个问题中,我正在尝试缓存一个单一的值,我们称其为foo。如果该值没有被缓存,那么检索需要一段时间。
我的问题不在于实现它,而是在于测试它。
为了测试它,我使用Task.WhenAll()同时启动5个任务来获取缓存值。第一个任务异步进入锁并检索值,而其他4个线程应该等待锁。等待后,他们应该逐个重新检查缓存的值,并发现它已经被第一个缓存它的线程检索过,然后返回它而不会再次检索。
然而,从测试的输出结果可以看出,它并不像我期望的那样。看起来只有2个线程同时执行,而其他线程则等待前两个完成后才能进入GetFoo()方法。
为什么会发生这种情况?是因为我在VS单元测试中运行它吗?请注意,我的测试仍然通过了,但不是我期望的方式。我怀疑在VS单元测试中有一些限制线程数量的限制。
我的问题不在于实现它,而是在于测试它。
为了测试它,我使用Task.WhenAll()同时启动5个任务来获取缓存值。第一个任务异步进入锁并检索值,而其他4个线程应该等待锁。等待后,他们应该逐个重新检查缓存的值,并发现它已经被第一个缓存它的线程检索过,然后返回它而不会再次检索。
[TestClass]
public class Class2
{
private readonly Semaphore semaphore = new Semaphore(1, 1);
private bool? foo;
private async Task<bool> GetFoo()
{
bool fooValue;
// Atomic operation to get current foo
bool? currentFoo = this.foo;
if (currentFoo.HasValue)
{
Console.WriteLine("Foo already retrieved");
fooValue = currentFoo.Value;
}
else
{
semaphore.WaitOne();
{
// Atomic operation to get current foo
currentFoo = this.foo;
if (currentFoo.HasValue)
{
// Foo was retrieved while waiting
Console.WriteLine("Foo retrieved while waiting");
fooValue = currentFoo.Value;
}
else
{
// Simulate waiting to get foo value
Console.WriteLine("Getting new foo");
await Task.Delay(TimeSpan.FromSeconds(5));
this.foo = true;
fooValue = true;
}
}
semaphore.Release();
}
return fooValue;
}
[TestMethod]
public async Task Test()
{
Task[] getFooTasks = new[] {
this.GetFoo(),
this.GetFoo(),
this.GetFoo(),
this.GetFoo(),
this.GetFoo(),
};
await Task.WhenAll(getFooTasks);
}
}
在我的实际测试和生产代码中,我通过接口检索值,并使用Moq模拟该接口。在测试结束时,我验证接口只被调用了1次(通过),而不是>1次(失败)。
输出:
Getting new foo
Foo retrieved while waiting
Foo already retrieved
Foo already retrieved
Foo already retrieved
然而,从测试的输出结果可以看出,它并不像我期望的那样。看起来只有2个线程同时执行,而其他线程则等待前两个完成后才能进入GetFoo()方法。
为什么会发生这种情况?是因为我在VS单元测试中运行它吗?请注意,我的测试仍然通过了,但不是我期望的方式。我怀疑在VS单元测试中有一些限制线程数量的限制。