首页 > 解决方案 > 监视器输入 ArgumentNullException

问题描述

我试图理解为什么我在方法上遇到ArgumentNull异常。Monitor.Enter调用代码仅使用NamedLocker. 我正在使用锁来防止访问共享资源。正在处理的信号量是否会导致空引用异常?

System.ArgumentNullException: Value cannot be null.
at System.Threading.Monitor.Enter(Object obj)
at System.Threading.SemaphoreSlim.<WaitUntilCountOrTimeoutAsync>d__31.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

WaitAsync在获得Semaphore.
以下是我的代码:

public class NamedLocker
{
    private static readonly Dictionary<string, SemaphoreSlim> _lockDict =
        new Dictionary<string, SemaphoreSlim>();
    private readonly Config config;
    private static readonly object semLock = new object();
    public NamedLocker(Config config)
    {
        this.config = config;
    }

    public async Task RunWithLock(string name, Func<Task> body)
    {
        try
        {
            _ = await GetOrCreateSemaphore(name)
               .WaitAsync(TimeSpan.FromSeconds(5))
               .ConfigureAwait(false);
            await body().ConfigureAwait(false);
        }
        finally
        {
            _ = Task.Run(async () =>
            {
                await Task.Delay(this.config.LockDelayMilliseconds)
                    .ConfigureAwait(false);
                RemoveLock(name);
            });
        }
    }

    public void RemoveLock(string key)
    {
        SemaphoreSlim outSemaphoreSlim;
        lock (semLock)
        {
            if (_lockDict.TryGetValue(key, out outSemaphoreSlim))
            {
                _lockDict.Remove(key);
                outSemaphoreSlim.Release();
                outSemaphoreSlim.Dispose();
            }
        }
    }

    private SemaphoreSlim GetOrCreateSemaphore(string key)
    {
        SemaphoreSlim outSemaphoreSlim;
        lock (semLock)
        {
            if (!_lockDict.TryGetValue(key, out outSemaphoreSlim))
            {
                outSemaphoreSlim = new SemaphoreSlim(1, 1);
                _lockDict[key] = outSemaphoreSlim;
            }
            return outSemaphoreSlim;
        }
    }
}

这是我用来验证异常的测试:

[TestMethod]
public async Task PPP()
{
    var namedLocker = new NamedLocker(new Config
    {
        LockDelayMilliseconds = 100
    });

    Func<Task> funcToRunInsideLock = async () => await Task.Delay(300);
    var tasks = Enumerable.Range(0, 100).Select(
        i => namedLocker.RunWithLock((i % 2).ToString(), funcToRunInsideLock));
    await Task.WhenAll(tasks).ConfigureAwait(false);
}

编辑:删除Dispose()infinally块(inside RemoveLock)使其在没有空异常的情况下工作。我不明白为什么会这样。

标签: c#concurrencylockingsemaphore

解决方案


推荐阅读