长期访问共享资源,能够根据请求发布

时间:2017-07-26 09:14:29

标签: c# .net locking

我不确定如何最好地解释我想要的东西,所以让我写一些说明性的代码。基本上:后台线程遍历大量工作,但在极少数情况下需要中断。

// This loop runs in a long-running thread that does
// a lot of high-priority work on a shared resource.
// The vast majority of time it is the only thread accessing the resource.
private void FastLoop()
{
    lock (locker)
    {
        while (true)
        {
            DoWork();

            // Somehow checking if another thread needs the shared resource.
            // Ideally I'd like something like this,
            // but alas: Monitor.BlockedCount() doesn't exist.
            while (Monitor.BlockedCount(locker) > 0)
                Monitor.Wait(locker);
        }
    }
}

// Methods like this get called rarely, but there is many of them
// so the code required to lock and unlock from secondary threads
// should be simple.
private void OtherThreads()
{
    lock (locker)
    {
        DoSomethingElse();
        Monitor.Pulse(locker);
    }
}

当前实现使用最简单的方法,基本上只是快速解锁并再次锁定,以便为其他线程提供跳入的机会。

private void FastLoopCurrentImplementation()
{
    while (true)
    {
        lock (locker)
            DoWork();
    }
}
private void OtherThreadsCurrentImplementation()
{
    lock (locker)
        DoSomethingElse();
}

有些东西告诉我这种简单化的方法可能会有问题。如果我没有弄错的话,即使另一个线程被阻止,循环也可以解锁并立即再次锁定。这意味着其他方法可能会花费多个主线程循环周期。

我认为这种方法不理想吗?什么是锁定/解锁的首选方法?

1 个答案:

答案 0 :(得分:1)

您可以自己统计阻止的线程:

private long blockedCount = 0;

private void FastLoop()
{
    lock (locker)
    {
        while (true)
        {
            DoWork();

            while (Interlocked.Read(ref blockedCount) > 0)
                Monitor.Wait(locker);
        }
    }
}

private void OtherThreads()
{
    try
    {
        var wasBlocked = false;
        if (!Monitor.TryEnter(locker))
        {
            wasBlocked = true;
            Interlocked.Increment(ref blockedCount);
            Monitor.Enter(locker);
        }

        DoSomethingElse();

        if (wasBlocked)
            Interlocked.Decrement(ref blockedCount);

        Monitor.Pulse(locker);
    }
    finally
    {
        Monitor.Exit(locker);
    }
}