Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中编写条件锁?_C#_.net_Multithreading_Locking - Fatal编程技术网

如何在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