Iai*_*ser 10 c# multithreading thread-safety
在使用线程安全时,我发现自己总是在执行锁定块中的代码之前"仔细检查",我想知道我是否正在做正确的事情.考虑以下三种做同样事情的方法:
例1:
private static SomeCollection MyCollection;
private static Object locker;
private void DoSomething(string key)
{
if(MyCollection[key] == null)
{
lock(locker)
{
MyCollection[key] = DoSomethingExpensive();
}
}
DoSomethingWithResult(MyCollection[key]);
}
Run Code Online (Sandbox Code Playgroud)
例2:
private static SomeCollection MyCollection;
private static Object locker;
private void DoSomething(string key)
{
lock(locker)
{
if(MyCollection[key] == null)
{
MyCollection[key] = DoSomethingExpensive();
}
}
DoSomethingWithResult(MyCollection[key]);
}
Run Code Online (Sandbox Code Playgroud)
例3:
private static SomeCollection MyCollection;
private static Object locker;
private void DoSomething(string key)
{
if(MyCollection[key] == null)
{
lock(locker)
{
if(MyCollection[key] == null)
{
MyCollection[key] = DoSomethingExpensive();
}
}
}
DoSomethingWithResult(MyCollection[key]);
}
Run Code Online (Sandbox Code Playgroud)
我总是倾向于示例3,这就是为什么我认为我做的是正确的事情
DoSomething(string)MyCollection[key] == null 所以线程1获得一个锁,就像线程2进入一样MyCollection[key] == null 仍然是,因此线程2等待获取锁定MyCollection[key]并将其添加到集合中DoSomethingWithResult(MyCollection[key]);MyCollection[key] != null示例1可行,但线程2可能存在冗余计算的风险MyCollection[key].
示例2可以工作,但是每个线程都会获得一个锁,即使它不需要 - 这可能是一个(无可否认的很小)瓶颈.如果你不需要,为什么要保留线程?
我是否在思考这个问题,如果是这样,处理这些情况的首选方式是什么?
不应使用第一种方法.正如您所意识到的那样,它会泄漏,因此不止一个线程最终会运行昂贵的方法.该方法所用的时间越长,另一个线程也会运行它的风险就越大.在大多数情况下,它只是一个性能问题,但在某些情况下,结果数据稍后会被一组新数据替换也可能是一个问题.
第二种方法是最常用的方法,如果数据被频繁访问以致锁定成为性能问题,则使用第三种方法.