Rya*_*dge 4 c# caching semaphore locking task
在这个问题中,我试图缓存单个值,让我们称之为foo.如果未缓存该值,则需要一段时间才能检索.
我的问题不是实现它,而是测试它.
为了测试它,我使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
在我的实际测试和生产代码中,我通过接口检索值并使用Moq模拟该接口.在测试结束时,我验证接口仅被调用1次(通过),而不是> 1次(失败).
输出:
Getting new foo
Foo retrieved while waiting
Foo already retrieved
Foo already retrieved
Foo already retrieved
Run Code Online (Sandbox Code Playgroud)
但是你可以从测试的输出中看出它并不像我预期的那样.看起来好像只有2个线程同时执行,而其他线程等到前两个线程完成甚至进入 GetFoo()方法.
为什么会这样?是因为我在VS单元测试中运行吗?请注意,我的测试仍然通过,但不是我预期的方式.我怀疑VS单元测试中的线程数有一些限制.
Task.WhenAll()不会启动任务 - 它只是等待它们.
同样,调用async方法实际上并不强制并行化 - 它不会引入新线程或类似的东西.在以下情况下,您只能获得新线
Task.Run,并且任务计划程序创建一个新线程来运行它.(当然,这可能不需要.)说实话,在异步方法中使用阻塞 Semaphore方法对我来说是非常错误的.你似乎并没有真正接受异步的想法...我没有试图准确分析你的代码将要做什么,但我认为你需要阅读更多关于如何async工作,以及如何最好地使用它.