非阻塞锁定
本文关键字:锁定 | 更新日期: 2023-09-27 18:23:57
我想为一个重复操作启动一些新线程。但当这样的操作已经在进行时,我想放弃当前的任务。在我的场景中,我只需要非常当前的数据——丢弃的数据不是问题。
在MSDN中,我找到了Mutex
类,但据我所知,它会等待轮到它,阻塞当前线程。我还想问你:.NET框架中是否已经存在某种东西,它会执行以下操作:
- 某个方法M是否已经在执行
- 如果是,
return
(让我增加一些计数器用于统计) - 如果没有,请在新线程中启动方法M
您可能遇到过的lock(someObject)
语句是围绕Monitor.Enter
和Monitor.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
]的问题
否-不要在上使用this
到lock
。创建一个私有范围的对象作为锁。
否则,您将面临以下潜在问题:
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
的东西可能会有所帮助,因为它是线程安全的,而且大多是无锁的。。。
另一种选择是使用ConcurrentQueue
或ConcurrentStack
。。。
您可能会在以下网站上找到一些有用的信息(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;
}