非阻塞锁定

本文关键字:锁定 | 更新日期: 2023-09-27 18:23:57

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

在MSDN中,我找到了Mutex类,但据我所知,它会等待轮到它,阻塞当前线程。我还想问你:.NET框架中是否已经存在某种东西,它会执行以下操作

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

非阻塞锁定

您可能遇到过的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();
    }  
}

在上面的例子中,一些外部代码仅仅通过对外部可访问的东西进行锁定,就成功地破坏了类的内部锁定。

最好创建一个您可以控制的私有对象,并且类外没有人可以访问该对象,以避免此类问题;这包括不使用CCD_ 10或类型本身CCD_。

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

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

另一种选择:

从你的描述来看,你似乎有一个生产者-消费者场景。。。

在这种情况下,使用类似BlockingCollection的东西可能会有所帮助,因为它是线程安全的,而且大多是无锁的。。。

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

您可能会在以下网站上找到一些有用的信息(PDf也可以下载-最近我自己下载了它)。Adavced线程Suspend和Resume或Aborting章节可能就是您所处的位置。

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

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;
}