如何在C#中编写条件锁?
问题是我一直在使用来保护代码的关键部分,但现在,我意识到如果满足某些条件,我可以允许并发执行该关键代码。如何在C#中编写条件锁?,c#,.net,multithreading,locking,C#,.net,Multithreading,Locking,问题是我一直在使用来保护代码的关键部分,但现在,我意识到如果满足某些条件,我可以允许并发执行该关键代码。 有没有办法调节锁?我猜您有一些代码看起来有点像这样: Action doThatThing = someMethod; if (condition) { lock(thatThing) { doThatThing(); } } else { doThatThing(); } private Monkey GetScaryMonkey(int numberOfHe
有没有办法调节锁?我猜您有一些代码看起来有点像这样:
Action doThatThing = someMethod;
if (condition)
{
lock(thatThing)
{
doThatThing();
}
}
else
{
doThatThing();
}
private Monkey GetScaryMonkey(int numberOfHeads){
Monkey ape = null;
lock(this) {
ape = new Monkey();
ape.AddHeads(numberOfHeads);
}
return ape;
}
要使此有条件,您不能这样做:
private Monkey GetScaryMonkey(int numberOfHeads){
if ( numberOfHeads > 1 ) {
lock(this) {
return CreateNewMonkey( numberOfHeads );
}
}
return CreateNewMonkey( numberOfHeads );
}
应该行得通,不是吗?我想那个问题叫“比赛条件!”。如果在检查后不久,但在线程进入代码的关键部分之前,条件从true变为false,该怎么办?或者当一个线程正在执行它时?实际上,为了避免争用情况,我会尝试使用一个here-treat并发访问作为读锁,独占访问作为写锁。这样,如果条件发生变化,您就不会在该区域盲目地执行某些不适当的代码(错误地认为它是安全的);有点冗长,但是 (格式为空格):
编辑:是的,存在争用条件,除非您可以确保在线程进入时该条件保持不变。我不是线程专家,但听起来您可能正在寻找类似的东西(双重检查锁定)。这样做的目的是在获取锁之前和之后检查状态
private static object lockHolder = new object();
if (ActionIsValid()) {
lock(lockHolder) {
if (ActionIsValid()) {
DoSomething();
}
}
}
使用,如上所述。这就是诀窍:)
确保您的锁定对象是静态的,如not.that.dave.foley.myopenid.com的示例中所列。如果您有许多需要条件锁定的方法/属性,您不希望重复相同的模式。我提出以下建议: 非重复条件锁模式 使用私有助手
struct
实现IDisposable
我们可以封装条件/锁,而不会产生可测量的开销
public void DoStuff()
{
使用(ConditionalLock())
{
//线程安全代码
}
}
它很容易实现。下面是演示此模式的示例类:
公共类计数器
{
私有静态只读int MAX_COUNT=100;
私有只读bool同步;
私人整数计数;
私有只读对象lockObject=新对象();
私人内部锁计数;
公共计数器(bool同步)
{
this.synchronized=synchronized;
}
公共整数计数
{
得到
{
使用(ConditionalLock())
{
返回计数;
}
}
}
公共整数锁计数
{
得到
{
使用(ConditionalLock())
{
返回锁计数;
}
}
}
公共空间增加()
{
使用(ConditionalLock())
{
如果(计数<最大计数)
{
睡眠(10);
++计数;
}
}
}
private LockHelper ConditionalLock()=>new LockHelper(此);
//这就是魔法发生的地方!
私有只读结构LockHelper:IDisposable
{
专用只读计数器;
私有只读布尔锁;
公共锁具助理(柜位)
{
this.counter=计数器;
锁定=错误;
如果(计数器已同步)
{
Monitor.Enter(counter.lockObject,ref lockTake);
counter.lockCount++;
}
}
私有无效退出()
{
如果(已锁定)
{
监视器。退出(计数器。锁定对象);
}
}
void IDisposable.Dispose()=>Exit();
}
}
现在,让我们创建一个小示例程序来演示其正确性
类程序
{
静态void Main(字符串[]参数)
{
var onlyOnThisThread=新计数器(synchronized:false);
增加最大值(c1);
var onManyThreads=新计数器(synchronized:true);
var t1=Task.Factory.StartNew(()=>IncreaseToMax(c2));
var t2=Task.Factory.StartNew(()=>IncreaseToMax(c2));
var t3=Task.Factory.StartNew(()=>IncreaseToMax(c2));
任务。等待(t1、t2、t3);
WriteLine($“Counter(false)=>Count={c1.Count},LockCount={c1.LockCount}”);
WriteLine($“Counter(true)=>Count={c2.Count},LockCount={c2.LockCount}”);
}
专用静态无效增量最大(计数器)
{
对于(int i=0;i<1000;i++)
{
计数器增加();
}
}
}
输出:
现在,您可以让调用者决定是否需要锁定(代价高昂)。lock(这)不好:@spoolson-正是我要说的-p+1提到了可怕的猴子,我认为它们在现代编程讨论中很少被提起。@Seba-如果线程A决定它不需要锁,但线程B后来决定它需要锁,该怎么办?你有一个奇怪的锁定/非锁定组合。因此,读写器锁似乎很合适。如果条件在应用程序的生命周期中保持不变,这也没关系。。。也许是一种配置。请继续阅读本文中的其他答案,以获得更好的解决方案。双重检查锁定应该是你想要的答案。这并不能提供问题的答案。若要评论或要求作者澄清,请在其帖子下方留下评论。-@maxhb我不要求在这个回答中澄清。不要让问号迷惑你。重点是:这不仅仅是在输入的时候——如果条件发生变化,那么这可能意味着没有锁定的代码正在冒险运行。如果你知道所有线程都已经输入了,那么改变条件是安全的。但你不太可能确定这一点。对不起,我不同意;如果“条件”的含义是线程应该同步,那么它必须应用于关键部分的整个执行,而不是j
bool locked = false;
if (condition) {
Monitor.Enter(lockObject);
locked = true;
}
try {
// possibly critical section
}
finally {
if (locked) Monitor.Exit(lockObject);
}
private static object lockHolder = new object();
if (ActionIsValid()) {
lock(lockHolder) {
if (ActionIsValid()) {
DoSomething();
}
}
}
Counter(false) => Count = 100, LockCount = 0
Counter(true) => Count = 100, LockCount = 3002