27

由于ReaderWriterLockSlim该类使用线程 ID 来查看谁拥有锁,因此在不保证所有方法都将在同一个线程上执行的异步方法中使用它是安全的。

例如。

    System.Threading.ReaderWriterLockSlim readerwriterlock = new System.Threading.ReaderWriterLockSlim();
    private async Task Test()
    {
        readerwriterlock.EnterWriteLock();
        await Task.Yield(); //do work that could yield the task
        readerwriterlock.ExitWriteLock(); //potentailly exit the lock on a different thread
    }
4

2 回答 2

28

在异步方法中使用 ReaderWriterLockSlim 是否安全

是和不是。在异步方法中使用它可能是安全的,但在异步方法中使用它可能不安全,在这种方法中你进入和退出跨越await.

在这种情况下,不,这不一定是安全的。

ExitWriteLock必须从调用EnterWriteLock. 否则,它会抛出一个SynchronizationLockException. 根据文档,在以下情况下会引发此异常:

当前线程还没有进入写模式的锁。

唯一安全的情况是,如果在异步方法中使用该方法,该方法始终处于存在电流的环境中,该电流SynchronizationContext会将事物移回同一线程(即:Windows 窗体、WPF 等),并且没有被嵌套异步调用使用,其中调用链上的“父级”设置了一个任务ConfigureAwait(false)(这将阻止Task捕获同步上下文)。如果您处于该特定场景中,您会知道线程将被维护,因为await调用会将您编组回调用上下文。

于 2013-04-08T15:15:48.373 回答
26

不,不应像在您的示例中那样使用线程仿射协调原语。

您正确地确定了在await. 由于方法提前返回的方式,还有另一个问题async:调用者不知道锁已被持有。

ReaderWriterLockSlim默认情况下是一个非递归锁,所以如果另一个async方法试图获取相同的锁,你会得到一个死锁。即使你让锁递归,你仍然会遇到一个问题:在持有锁的时候永远不应该调用任意的最终用户代码,这就是你在使用await.

SemaphoreSlim类型是感知型的async(通过它的WaitAsync方法),Stephen Toub在我的AsyncEx 库中也提供了一系列async协调原语

于 2013-04-08T15:35:13.860 回答