SemaphoreSlim 的超时是否违背了它自己的目的?

Does SemaphoreSlim's timeout defeat its own purpose?

semaphore的真正力量是:

Limits the number of threads that can access a resource or pool of resources concurrently

这个理解清楚了。

但是我从来没有机会玩过接受超时整数的 Wait 的重载,但是 - 这似乎 允许 多个线程进入临界状态尽管我已明确设置信号量一次不允许多个线程:

private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);

private void Main()
{
    Task.Run(() => DelayAndIncrementAsync());
    Task.Run(() => DelayAndIncrementAsync());
}

private void DelayAndIncrementAsync()
{
    _mutex.Wait(2000);

    try
    {
        Console.WriteLine(0);
        Thread.Sleep(TimeSpan.FromSeconds(5));
        Console.WriteLine(1);
    }
    finally
    {
        _mutex.Release();
    }
}

第一个线程正在进入互斥区,打印“0”,等待 5 秒,同时 2 秒后另一个线程也进入临界区?

问题

这不是违背信号量的全部目的吗?

我会使用此超时的现实生活场景是什么,尤其是当基本规则是 -

"Semaphore = Limits the number of threads that can access a resource or pool of resources concurrently

您需要检查等待的 return 值。基于超时的等待将尝试 2 秒来获取互斥锁,然后 return。您需要检查 return 值是否为真(即您有互斥锁)。

编辑:另请记住,如果信号量可用,基于超时的等待将立即return,因此您不能通过此技术使用它来防止代码中的无限循环。

private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);
void Main()
{

    Task.Run(()=>DelayAndIncrementAsync());
    Task.Run(()=>DelayAndIncrementAsync());
}
public   void  DelayAndIncrementAsync()
{
    if (_mutex.Wait(2000))
    {
        try
        {
            Console.WriteLine(0);
            Thread.Sleep(TimeSpan.FromSeconds(5));
            Console.WriteLine(1);
        }    
        finally
        {
            _mutex.Release();
        }
    } else {
        //oh noes I don't have the mutex
    }
}

你的误解是有一个隐含的 "mutex zone" 不是你定义的。

您正在使用的 Wait 的重载 returns 一个布尔值,告诉您互斥量是否已成功输入。

你在你的例子中所做的是进入临界区,无论线程是否已经获得互斥量,使其变得多余。

一般来说,如果您想 尝试 进入互斥锁,但又想在当前不可能的情况下使用后备策略,则您可能希望在任何情况下使用此重载在规定的时间内获取互斥量。

这会让人们畏缩,但使用超时(并确认它确实超时)是记录和跟踪死锁错误的好方法。当然,如果你正确地编写了你的​​程序,你就不需要这些,但我个人将其用于此目的,这为我节省了很多时间。

所以是的,如果你让它超时然后用多个线程命中关键部分,它确实会破坏目的(在大多数情况下)。但记录或检测死锁错误可能很有用。

还有一些用例,您希望多个线程访问临界区,但仅限于特定场景。例如,它不会是致命的,只是因为它的发生是不受欢迎的。例如,您不是使用信号量来阻止跨线程崩溃,而是使用其他东西。