链锁?

时间:2016-06-22 09:29:22

标签: c# multithreading async-await

当试图回答以下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时会抛出异常。

如何确保监视器的EnterExit方法在同一个线程上运行?

1 个答案:

答案 0 :(得分:3)

不是将锁定操作强制到几乎不可能的同一个线程上,而是使用非线程仿真的锁:SemaphoreSlim。它也具有本机异步支持(与阻止相反)。

在您与我关联的原始问题中,请改为使用this answer。似乎比包含大量人工复杂性的链解决方案更清洁。代码质量与使用的特定调用语法无关。只需将事物放在句法链中,就无法大大降低复杂性。

特别是链解决方案只是我认为Lock(new [] { lock1, lock2 }, () => ...);的复杂方式。所有链都会建立一个列表。 using使这更简单,因为它消除了lambda。 Lambda是不太可组合的,因为你不能像using那样从lambda返回。我认为你应该以此为目标:

using (MultiLock(new [] { lock1, lock2 }, timeout)) {
 //...
}