c#中的条件线程锁

问题描述 投票:0回答:2

当基础条件不恒定时是否可以有条件线程锁?

我有两个函数A和B,以及一个决定执行哪个函数的条件。 A本身是线程安全的,对A的多个调用可以同时执行,B则不是,并且是同步的。但在执行 B 期间,条件可能会发生变化(从 false 变为 true),因此当时执行 A 的所有线程都会抛出错误。

if (condition)
{
    A();
}
else
{
    B();
}

A - thread safe
B - Synchronized using [MethodImpl(MethodImplOptions.Synchronized)]

因此,我正在寻找一种方法来锁定 A,但仅在 B 运行时锁定。 请建议一种方法来实现这一目标。

一些阐述: 我正在创建一个缓存,性能非常关键,因此毯子锁不可行。

条件是请求的数据是否存在于缓存中。

A() = AddToUpdates() - 在缓存命中时执行,仅使用并发字典添加特定缓存键的更新数量。

B() = ProccessUpdates() 和 EvictLeastPriorityEntry() - 在缓存未命中时执行,将处理所有先前的更新,并且将重新安排存储缓存条目顺序的底层数据结构。

然后优先级最低的条目将被删除。

正如已接受的答案中所提到的,ReaderWriterLock 似乎是正确的选择。

不过有一个问题, 假设,线程 1 开始执行并且发生缓存命中(在优先级最低的条目上),这意味着 if 条件为 true 并进入 if 块。但在调用A()之前,控制权切换到thread2。

thread2 - 发生缓存未命中,执行重新排序和逐出(来自 thread1 的 A() 需要访问的条目)。

现在当controller返回到thread1时,就会出现错误。

这是我认为应该有效的解决方案:

_lock.EnterReadLock();
if (condition)
{
    A();
}
_lock.ExitReadLock();

if (!condition)
{
    B();
}

void A()
{
   // ....
}

void B()
{
    _lock.EnterWriteLock();
    // ...
    _lock.ExitWriteLock();
}

这行得通吗?

谢谢你。

c# multithreading concurrency locking
2个回答
3
投票

您的问题的可能解决方案可能是

ReaderWriterLockSlim
类。这是一种同步原语,允许多个并发读取器或一个独占写入器,但不能同时支持这两者。

使用

ReaderWriterLockSlim
来保护一次由多个线程读取并由一个线程写入的资源。
ReaderWriterLockSlim
允许多个线程处于读模式,允许一个线程处于具有独占锁所有权的写模式,并允许一个具有读访问权限的线程处于可升级读模式,从该模式该线程可以升级为写模式,而不必放弃对资源的读取访问权限。

示例:

private readonly ReaderWriterLockSlim _lock = new();

void A()
{
    _lock.EnterReadLock();
    try
    {
        //...
    }
    finally { _lock.ExitReadLock(); }
}

void B()
{
    _lock.EnterWriteLock();
    try
    {
        //...
    }
    finally { _lock.ExitWriteLock(); }
}

2
投票

你的问题看起来很像这样:

  • A() 是一些只读方法,因此线程安全。 A 的不同执行并行是可以的。

  • B() 就像编写/改变 A 方法使用的东西。因此,如果同时执行,A() 就变得不是线程安全的。

例如,B() 可以写入列表,而 A() 可以在此列表上执行读取。你会得到 A() 抛出的异常“InvalidOperationException: Collection Was Modified”。

我建议你在谷歌中寻找“生产者/消费者问题”并寻找大量的例子。

但是如果你绝对想在 A 执行尚未终止时开始 B 执行,你可以使用 Monitor 类在 A() 中添加检查点,它用于锁定资源并与其他线程同步。但它更复杂,我会首先选择生产者/消费者模式,看看它是否满足需求

还有一些事情:

  • 我会检查 BlockingCollection 类的使用是否也适合您的确切需求(并且易于使用)

  • 不建议使用MethodImplOptions.Synchronized,因为它使用了公共锁。我们通常使用私有锁(

    object readonly _lock = new object();
    ),这样除了这个对象的维护者之外没有人可以锁定它,从而防止死锁(并防止其他人指责你的代码有错误,因为其他人在不知道的情况下锁定了你的类实例你在内部也做同样的事情)

© www.soinside.com 2019 - 2024. All rights reserved.