.net C#-只允许处理一个锁,让所有其他锁等待,然后退出而不执行

.net C#-只允许处理一个锁,让所有其他锁等待,然后退出而不执行,.net,multithreading,locking,.net,Multithreading,Locking,我有一个多用户可以调用的流程。这是一个非常昂贵的查询,但应该只需要每隔5分钟左右运行一次即可刷新一些存档数据。我现在有一个锁,这样我们就不会一次运行这个进程好几次,这会使系统崩溃,但是每个用户都必须等待前面的锁运行,然后才能运行。如果有3-4个用户等待锁定,第4个用户必须等待20分钟以上才能运行查询 我想做的是锁定这个对象,并在第一个请求上运行查询。如果有其他请求进入,让它们等待当前锁完成,然后返回,而不实际执行查询 Net中是否有内置的东西可以实现这一点,或者我是否需要为此锁编写一些特定的代码

我有一个多用户可以调用的流程。这是一个非常昂贵的查询,但应该只需要每隔5分钟左右运行一次即可刷新一些存档数据。我现在有一个锁,这样我们就不会一次运行这个进程好几次,这会使系统崩溃,但是每个用户都必须等待前面的锁运行,然后才能运行。如果有3-4个用户等待锁定,第4个用户必须等待20分钟以上才能运行查询

我想做的是锁定这个对象,并在第一个请求上运行查询。如果有其他请求进入,让它们等待当前锁完成,然后返回,而不实际执行查询

Net中是否有内置的东西可以实现这一点,或者我是否需要为此锁编写一些特定的代码?

您可以使用和锁来实现这一点

private object _dataLock = new object();
private ManualResetEvent _dataEvent = new ManualResetEvent(false);

private ArchiveData GetData()
{
    if (Monitor.TryEnter(_dataLock))
    {
        _dataEvent.Reset();  // makes other threads wait on the data

        // perform the query

        // then set event to say that data is available
        _dataEvent.Set();
        try
        {
            return data;
        }
        finally
        {
            Monitor.Exit(_dataLock);
        }
    }

    // Other threads wait on the event before returning data.
    _dataEvent.WaitOne();
    return data;
}
因此到达那里的第一个线程获得锁并清除
\u dataEvent
,这表示其他线程将不得不等待数据。这里有一个竞争条件,如果第二个客户端在重置
\u dataEvent
之前到达那里,它将返回旧数据。我认为这是可以接受的,考虑到这是存档数据,而且发生这种情况的机会窗口非常小

通过的其他线程尝试获得锁,失败,并被
WaitOne
阻止

当数据可用时,执行查询的线程设置事件、释放锁并返回数据


请注意,我没有将整个锁体放入
try…finally
中。有关原因,请参阅Eric Lippert的。

此解决方案适用于不能接受多个调用方执行“准备代码”的可能性的用户

这种技术避免了在“正常”用例场景中为数据准备好时使用锁。锁定确实有一些开销。这可能适用于您的用例,也可能不适用于您的用例

该模式称为
if锁if
模式,IIRC。我已尽我所能尝试对内联进行注释:

bool dataReady;
string data;
object lock = new object();

void GetData()
{
    // The first if-check will only allow a few through. 
    // Normally maybe only one, but when there's a race condition 
    // there might be more of them that enters the if-block. 
    // After the data is ready though the callers will never go into the block, 
    // thus avoids the 'expensive' lock.
    if (!dataReady)
    {
        // The first callers that all detected that there where no data now
        // competes for the lock. But, only one can take it. The other ones
        // will have to wait before they can enter. 
        Monitor.Enter(lock);
        try
        {
            // We know that only one caller at the time is running this code
            // but, since all of the callers waiting for the lock eventually
            // will get here, we have to check if the data is still not ready.
            // The data could have been prepared by the previous caller,
            // making it unnecessary for the next callers to enter.
            if (!dataReady)
            { 
                // The first caller that gets through can now prepare and 
                // get the data, so that it is available for all callers.
                // Only the first caller that gets the lock will execute this code.
                data = "Example data";

                // Since the data has now been prepared we must tell any other callers that
                // the data is ready. We do this by setting the 
                // dataReady flag to true.
                Console.WriteLine("Data prepared!");
                dataReady = true;
            }
        }
        finally
        {
            // This is done in try/finally to ensure that an equal amount of 
            // Monitor.Exit() and Monitor.Enter() calls are executed. 
            // Which is important - to avoid any callers being left outside the lock. 
            Monitor.Exit(lock);
        }
    }

    // This is the part of the code that eventually all callers will execute,
    // as soon as the first caller into the lock has prepared the data for the others.
    Console.WriteLine("Data is: '{0}'", data);
}
MSDN参考:


这听起来像是一个需要更多解决方案的问题。我可能会开始考虑某种理智的缓存策略。我没有投反对票,但可能是那个投反对票的人希望你了解.net的互斥功能,并且已经从@Scottie回来了一些代码-我没有投反对票,但我希望如此,因为这个问题可能有点广泛,并且缺乏关于您当前解决方案的实现细节。