当试图回答以下question时,我写了这段代码:
using static MyNameSpace.Locker; //So that we don't need to specify the static class name before each call.
public class MainClass
{
public MainMethod()
{
Lock(new object()).Lock(new object()).RunAction(() => Console.WriteLine("Finished"));
}
}
public static class Locker
{
public static async Task<List<object>> Lock(object toLock, int timeout = -1)
{
await Task.Run(() => TryEnter(toLock, timeout));
return new List<object>() { toLock };
}
public static async Task<List<object>> Lock(
this Task<List<object>> lockedChain,
object toLock,
int timeout = -1)
{
await Task.Run(() => TryEnter(toLock, timeout));
await lockedChain;
lockedChain.Result.Add(toLock)
return lockedChain.Result;
}
public static async void RunAction(this Task<List<object>> lockChain, Action toRun)
{
await lockChain;
try
{
toRun.Invoke();
}
finally
{
foreach (var chainMember in lockChain.Result)
{
Monitor.Exit(chainMember);
}
}
}
private static void TryEnter(object toLock, int timeout = -1)
{
var success = false;
if (timeout > 0)
{
success = Monitor.TryEnter(toLock, timeout);
}
else
{
success = Monitor.TryEnter(toLock);
}
if (!success)
{
throw new TimeoutException();
}
}
}
但正如一些用户正确地指出的那样,这不会有一个非常简单的原因:由于这些方法是异步的,它们可能无法在同一个线程上运行,因此在尝试释放Monitor时会抛出异常。
如何确保监视器的Enter
和Exit
方法在同一个线程上运行?
答案 0 :(得分:3)
不是将锁定操作强制到几乎不可能的同一个线程上,而是使用非线程仿真的锁:SemaphoreSlim
。它也具有本机异步支持(与阻止相反)。
在您与我关联的原始问题中,请改为使用this answer。似乎比包含大量人工复杂性的链解决方案更清洁。代码质量与使用的特定调用语法无关。只需将事物放在句法链中,就无法大大降低复杂性。
特别是链解决方案只是我认为Lock(new [] { lock1, lock2 }, () => ...);
的复杂方式。所有链都会建立一个列表。 using
使这更简单,因为它消除了lambda。 Lambda是不太可组合的,因为你不能像using
那样从lambda返回。我认为你应该以此为目标:
using (MultiLock(new [] { lock1, lock2 }, timeout)) {
//...
}