【问题标题】:SemaphoreSlim not working under extreme conditions?SemaphoreSlim 不能在极端条件下工作?
【发布时间】:2014-03-18 14:15:34
【问题描述】:

我们正在使用 .NET 4.5.1 开发应用程序,并在后台使用 SemaphoreSlim 实现了我自己的“异步锁”。要锁定我们使用以下方法:

public async Task<IDisposable> LockAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    await SyncRoot.WaitAsync(cancellationToken).ConfigureAwait(false);
    return this;
}

其中SyncRootnew SemaphoreSlim(1) 实例。

这似乎在“生产”中运行良好,但它在以下单元测试中失败:

for (int i = 0; i < numberOfTasks; i++)
{
    tasks[i] = Task.Run<Task>(async () =>
    {
        Interlocked.Increment(ref counterAtomicBegin);
        using (await alock.LockAsync().ConfigureAwait(false))
        {
            counterLocked += 1;
        }
        Interlocked.Increment(ref counterAtomicEnd);
    });
}

counterLockedcounterAtomicBegin 最后不相等(在 100k 个任务时,它们相差约 1k)。

是我做错了什么还是SemaphoreSlim的问题?

更新:按照建议删除嵌套逻辑并调整文本。
请参阅以下代码(可以在 LINQPad 中执行)进行测试:http://pastebin.com/WXecZxqu

【问题讨论】:

    标签: c# .net asynchronous locking semaphore


    【解决方案1】:

    你的逻辑不正确。

    异步锁不是线程仿射的,因此线程持有锁的整个概念是不正确的。相反,代码的某个部分持有锁,并且该代码的一部分可能在不同的线程上执行。

    在我看来,recursive locks are a bad idea(链接是我的博客文章,其中包含详细信息)。理论上可以编写递归异步锁(我知道的唯一实现是part of the test suite for my AsyncEx library),但它只能在.NET 4.5 完整框架上工作,我仍然认为这是一个坏主意。

    【讨论】:

    • 因此,您的建议是即使在 async 方法中也使用普通的 lock 语句,或者只是扔掉“嵌套锁”(这不仅仅是“必备”的要求) ?
    • 我建议您只删除“嵌套锁”并使用SemaphoreSlimWaitAsync/Release。我确实喜欢有一个 IDisposable 包装器的想法,所以我会保留它。
    • 您的Dispose 方法应该只调用一次Release
    • 对不起,没有帮助...你能重现这个问题吗?
    • 你的测试也有问题:Task.Run&lt;Task&gt;应该是Task.Run。 Monitor 在这种情况下肯定不会有帮助,因为它是线程仿射的。
    猜你喜欢
    • 2018-07-12
    • 2014-04-21
    • 2020-04-19
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 2020-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多