问题
lock
uses this pattern
if(Monitor.Enter(lock))
try
{
...
}
finally { Monitor.Exit(lock); } // using this style to reduce post "height"
if we don't want to wait infinite we can provide timeout
if(!Monitor.TryEnter(lock, timeout))
throw new TimeoutException();
try
{
...
}
finally { Monitor.Exit(lock); }
I have scenario when method has to obtain multiple locks before it start doing anything. This looks awful:
if(!Monitor.TryEnter(lockA, timeout))
throw new TimeoutException();
try
{
if(!Monitor.TryEnter(lockB, timeout))
throw new TimeoutException();
try
{
if(!Monitor.TryEnter(lockC, timeout))
throw new TimeoutException();
try
{
... // more of such constructions
}
finally { Monitor.Exit(lockC); }
}
finally { Monitor.Exit(lockB); }
}
finally { Monitor.Exit(lockA); }
It has problems:
looks ugly (the method code is indented, imagine how it will looks for
lockZ
), can be solved by putting method code into another method.locking occurs synchronously, so the worst successful case may take time slightly less than a sum of all timeouts.
Is there a way to improve this timeout pattern?
I was thinking to make a method with delegate parameter and lock to achieve something like linq chaining (but to also run locks in parallel, this is a challenge):
Lock(lockA).Lock(lockB).Lock(lockC).Run( () => ...);
Or perhaps there is another way?
回答1:
I would go with usings for sync lock:
using System;
using System.Threading;
public class MyLock : IDisposable
{
private object lockObj;
public MyLock(object lockObj, TimeSpan timeout)
{
this.lockObj = lockObj;
if (!Monitor.TryEnter(this.lockObj, timeout))
throw new TimeoutException();
}
public void Dispose()
{
Monitor.Exit(lockObj);
}
}
Usage:
using(new MyLock(lockA, new TimeSpan.FromSeconds(1)))
using(new MyLock(lockB, new TimeSpan.FromSeconds(2)))
using(new MyLock(lockC, new TimeSpan.FromSeconds(3)))
{
// your code
}
Do not know if "locking" in ctor is good pattern / design, but it will work ;)
For async. parallelization is not good idea. Why?
If some thread will enter the monitor, the same thread must leave it (exit with lock). So if you lock on objA
within Parallel.ForEach
(f.e.) you will not know which thread has done it. So you won't be able to release it.
来源:https://stackoverflow.com/questions/37961521/locking-with-timeout-pattern