非阻塞锁定

时间:2012-02-09 10:58:12

标签: c# .net multithreading

我想为一个重复操作开始一些新线程。但是当这样的操作已经在进行中时,我想放弃当前的任务。在我的场景中,我只需要非常新的数据 - 丢弃的数据不是问题。

在MSDN中,我找到了Mutex类,但据我所知,它等待轮到它,阻塞当前线程。另外我想问你: .NET框架中是否存在某些内容,执行以下操作

  1. 某些方法M是否已被执行?
  2. 如果是,return(让我增加一些统计计数器)
  3. 如果没有,请在新线程中启动方法M

4 个答案:

答案 0 :(得分:24)

您可能遇到的lock(someObject)语句是围绕Monitor.EnterMonitor.Exit的语法糖。

但是,如果你以这种更详细的方式使用显示器,你也可以使用Monitor.TryEnter来检查你是否能够获得锁定 - 因此检查是否有其他人已经拥有它正在执行代码。

所以不要这样:

var lockObject = new object(); 

lock(lockObject)
{
    // do some stuff
}

尝试(选项1)

int _alreadyBeingExecutedCounter;
var lockObject = new object();

if (Monitor.TryEnter(lockObject))
{
   // you'll only end up here if you got the lock when you tried to get it - otherwise you'll never execute this code.

    // do some stuff

    //call exit to release the lock
    Monitor.Exit(lockObject);
}
else
{
    // didn't get the lock - someone else was executing the code above - so I don't need to do any work!
   Interlocked.Increment(ref _alreadyBeingExecutedCounter);
}

(你可能想在那里试一试确保锁定被释放)

或者完全免除显式锁定并执行此操作

(选项2)

private int _inUseCount;

public void MyMethod()
{
    if (Interlocked.Increment(ref _inUseCount) == 1)
    {
        // do dome stuff    
    }
    Interlocked.Decrement(ref _inUseCount);
}

[编辑:回答您关于this]的问题

否 - 请勿使用thislock。创建一个私有范围的对象作为你的锁。

否则你有这个潜在的问题:

public class MyClassWithLockInside
{
    public void MethodThatTakesLock()
    {
        lock(this)
        {
            // do some work
        }
    }
 }

public class Consumer
{
    private static MyClassWithLockInside _instance = new MyClassWithLockInside();

    public void ThreadACallsThis()
    {
          lock(_instance)
          {
              // Having taken a lock on our instance of MyClassWithLockInside,
              // do something long running
              Thread.Sleep(6000);
           }
    }

    public void ThreadBCallsThis()
    {
         // If thread B calls this while thread A is still inside the lock above,
         // this method will block as it tries to get a lock on the same object
         // ["this" inside the class = _instance outside]
         _instance.MethodThatTakesLock();
    }  
}

在上面的例子中,一些外部代码设法通过锁定外部可访问的东西来破坏我们类的内部锁定。

创建一个你控制的私有对象,以及你班级以外没有人可以访问的私有对象,以避免这些问题,这样做会好得多;这包括不使用this或类型本身typeof(MyClassWithLockInside)进行锁定。

答案 1 :(得分:2)

一种选择是使用重入哨兵:

您可以定义一个int字段(初始化为0)并在输入方法时通过Interlocked.Increment更新它,并且仅在它为1时才继续。最后只需执行Interlocked.Decrement

另一种选择:

根据您的描述,您似乎有一个Producer-Consumer-Scenario ......

对于这种情况,使用像BlockingCollection这样的东西可能会有所帮助,因为它是线程安全的并且几乎没有锁定......

另一种选择是使用ConcurrentQueueConcurrentStack ...

答案 2 :(得分:1)

您可能会在以下site上找到一些有用的信息(PDf也是可以下载的 - 最近我自己下载了它)。 Adavnced threading Suspend and Resume或Aborting章节可能是你不感兴趣的。

答案 3 :(得分:0)

您应该使用Interlocked类原子操作 - 以获得最佳性能 - 因为您实际上不会使用系统级同步(任何“标准”原语需要它,并涉及系统调用开销)。 //没有所有权的简单非重入互斥锁,很容易重制以支持//这些功能(只需在获取锁之后设置所有者(例如,将Thread引用与Thread.CurrentThread进行比较),并检查匹配的标识,为重入添加计数器) //不能使用bool,因为CompareExchange不支持它 private int lock;

public bool TryLock()
{
  //if (Interlocked.Increment(ref _inUseCount) == 1)      
  //that kind of code is buggy - since counter can change between increment return and
  //condition check - increment is atomic, this if - isn't.       
  //Use CompareExchange instead
  //checks if 0 then changes to 1 atomically, returns original value
  //return true if thread succesfully occupied lock
  return CompareExchange(ref lock, 1, 0)==0;
  return false;

}
public bool Release()
{
  //returns true if lock was occupied; false if it was free already
  return CompareExchange(ref lock, 0, 1)==1;
}