使用嵌套异步调用锁定

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用嵌套异步调用锁定相关的知识,希望对你有一定的参考价值。

我正在研究多线程WindowsPhone8应用程序,该应用程序在异步方法中具有关键部分。

有人知道在嵌套嵌套异步调用中C#中正确使用信号量/互斥锁的方法吗,其中内部方法可能会获得与调用堆栈中已经获得的锁相同的锁?我以为SemaphoreSlim可能是答案,但看起来会导致死锁。

public class Foo
{
    SemaphoreSlim _lock = new SemaphoreSlim(1);

    public async Task Bar()
    {
        await _lock.WaitAsync();

        await BarInternal();

        _lock.Release();
     }

    public async Task BarInternal()
    {
        await _lock.WaitAsync();  // deadlock

        // DO work

        _lock.Release();
     }

}
答案

递归锁是really bad idea(IMO;链接是我自己的博客)。对于async代码,这是尤其是 true。要使async兼容的递归锁起作用是很困难的。我有一个proof-of-concept here,但有一个合理的警告:我确实建议在生产中使用此代码,[此代码将not卷入AsyncEx,并且已进行not全面测试。

您应该做的是按照@svick的说明重组代码。像这样的东西:

public async Task Bar() { await _lock.WaitAsync(); await BarInternal_UnderLock(); _lock.Release(); } public async Task BarInternal() { await _lock.WaitAsync(); await BarInternal_UnderLock(); _lock.Release(); } private async Task BarInternal_UnderLock() { // DO work }

另一答案
这是我在这种情况下的工作(仍然,我对任务没有经验,所以不要打败我;-)因此,基本上,您已经将实际实现移至非锁定方法,并在所有获取锁定的方法中使用它们。

public class Foo { SemaphoreSlim _lock = new SemaphoreSlim(1); public async Task Bar() { await _lock.WaitAsync(); await BarNoLock(); _lock.Release(); } public async Task BarInternal() { await _lock.WaitAsync(); // no deadlock await BarNoLock(); _lock.Release(); } private async Task BarNoLock() { // do the work } }

另一答案
您可以使用具有支持递归标志的System.Threading.ReaderWriterLockSlimdoc):

ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); async Task Bar() { try { _lock.EnterReadLock(); await BarInternal(); } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } } async Task BarInternal() { try { _lock.EnterReadLock(); await Task.Delay(1000); } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } }

仍然要对递归非常小心,因为很难控制哪个线程获得了锁定以及何时获得锁定。

问题中的代码将导致死锁,因为它尝试两次获取锁,例如:

await _lock.WaitAsync(); await _lock.WaitAsync(); --> Will result in exception.

虽然在ReaderWriterLockSlim中标记了SupportsRecursion不会引发异常:

_lock.EnterReadLock(); _lock.EnterReadLock();

以上是关于使用嵌套异步调用锁定的主要内容,如果未能解决你的问题,请参考以下文章

在另一个单独的异步调用解决之前,如何防止/锁定函数返回?

尝试进行嵌套查询时已调用节点异步回调

onActivityResult 从未在我的嵌套片段中调用

锁定AppendAllText与TextWriter

从片段中调用分离的异步任务类

如何在嵌套异步函数中捕获错误