非阻塞锁定
我想为一个重复操作开始一些新的线程。 但是当这样的操作已经在进行中时,我想丢弃当前的任务。 在我的场景中,我只需要非常新的数据 – 丢弃的数据不是问题。
在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(); } }
在上面的例子中,一些外部代码设法通过锁定外部可访问的东西来破坏我们类的内部锁定。
创建一个你控制的私有对象,以及你class外没有人可以访问的私有对象要好得多,以避免这类问题; 这包括不使用this
或类型本身typeof(MyClassWithLockInside)
进行锁定。
一种选择是使用重入哨兵:
您可以定义一个int
字段(初始化为0)并在输入方法时通过Interlocked.Increment
更新它,并且只有在它为1时才继续。最后只需执行Interlocked.Decrement
。
另外一个选项:
根据您的描述,您似乎有一个Producer-Consumer-Scenario ……
对于这种情况,使用类似BlockingCollection
东西可能会有所帮助,因为它是线程安全的并且大多数是无锁的……
另一个选择是使用ConcurrentQueue
或ConcurrentStack
…
您可能会在以下站点上找到一些有用的信息(PDf也是可下载的 – 最近我自己下载了它)。 Adavnced threading Suspend and Resume或Aborting章节可能是你不感兴趣的。
您应该使用Interlocked类primefaces操作 – 以获得最佳性能 – 因为您实际上不会使用系统级同步(任何“标准”原语需要它,并涉及系统调用开销)。 //简单的不可重入的互斥锁,没有所有权,很容易重制以支持//这些function(只需在获取锁定后设置所有者(例如,将线程参考与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; }