我正在开发一个多线程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();
}
}
答案 0 :(得分:12)
递归锁是really bad idea(IMO;链接到我自己的博客)。对于async
代码,这是尤其 true。获得async
兼容的递归锁是很困难的。我有一个proof-of-concept here但是公平的警告:我不建议在生产中使用此代码,此代码将不转入AsyncEx,并且不彻底测试。
你应该做的是重构你的代码@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
}
答案 1 :(得分:7)
这就是我在这种情况下所做的事情(仍然,我没有任务经验,所以不要打败我;-)
所以基本上你已经将实际的实现移动到非锁定方法,并在所有获取锁定的方法中使用它们。
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
}
}
答案 2 :(得分:2)
首先,通读 Stephen Cleary 的博客文章,他在回答中链接到了该文章。他提到了多种原因,例如不确定的锁状态和不一致的不变量,这些都与递归锁相关联(更不用说递归异步锁了)。如果您能进行他和 Knickedi 在他们的回答中描述的重构,那就太好了。
但是,在某些情况下,这种类型的重构是不可能的。幸运的是,现在有多个库支持嵌套的async 调用(锁重入)。这里有两个。第一个的作者有一个 blog post 在这里他谈论了更多。
您可以将其合并到您的代码中(使用本示例中的第一个库):
public class Foo
{
AsyncLock _lock = new AsyncLock();
public async Task Bar()
{
// This first LockAsync() call should not block
using (await _lock.LockAsync())
{
await BarInternal();
}
}
public async Task BarInternal()
{
// This second call to LockAsync() will be recognized
// as being a reëntrant call and go through
using (await _lock.LockAsync()) // no deadlock
{
// do work
}
}
}
答案 3 :(得分:-1)
您可以使用具有支持递归标志的$ geom part list da0 | awk '/Mediasize:/ { print (++flag==1)?$2:"" } /[ ]type:/ { print (flag==1)?$2:"" }'
61872754688
freebsd-ufs
(doc):
System.Threading.ReaderWriterLockSlim
仍然要非常小心递归,因为很难控制哪个线程获取了锁定以及何时锁定。
问题中的代码将导致死锁,因为它尝试两次获取锁,例如:
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