- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
确保只有“后入”线程可以访问互斥锁/锁定区域而中间线程不获取锁的正确方法是什么?
示例序列:
A acquires lock
B waits
C waits
B fails to acquire lock*
A releases lock
C acquires lock
*B 应该无法通过异常(如 SemaphoreSlim.Wait(CancellationToken)
或 bool 型 Monitor.TryEnter()
类型构造)获取锁。
我可以想到几个类似的方案来实现这一点(例如使用 CancellationTokenSource
和 SemaphoreSlim
),但没有一个看起来特别优雅。
这种情况有通用的做法吗?
最佳答案
这应该可以像您希望的那样工作,它使用大小为 1 的 SemaphoreSlim 来控制它。我还添加了对传入 CancellationToken 以取消提前等待锁定的支持,它还支持 WaitAsync
返回任务而不是阻塞。
public sealed class LastInLocker : IDisposable
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
private CancellationTokenSource _cts = new CancellationTokenSource();
private bool _disposed = false;
public void Wait()
{
Wait(CancellationToken.None);
}
public void Wait(CancellationToken earlyCancellationToken)
{
if(_disposed)
throw new ObjectDisposedException("LastInLocker");
var token = ReplaceTokenSource(earlyCancellationToken);
_semaphore.Wait(token);
}
public Task WaitAsync()
{
return WaitAsync(CancellationToken.None);
}
public async Task WaitAsync(CancellationToken earlyCancellationToken)
{
if (_disposed)
throw new ObjectDisposedException("LastInLocker");
var token = ReplaceTokenSource(earlyCancellationToken);
//I await here because if ReplaceTokenSource thows a exception I want the
//observing of that exception to be deferred until the caller awaits my
//returned task.
await _semaphore.WaitAsync(token).ConfigureAwait(false);
}
public void Release()
{
if (_disposed)
throw new ObjectDisposedException("LastInLocker");
_semaphore.Release();
}
private CancellationToken ReplaceTokenSource(CancellationToken earlyCancellationToken)
{
var newSource = CancellationTokenSource.CreateLinkedTokenSource(earlyCancellationToken);
var oldSource = Interlocked.Exchange(ref _cts, newSource);
oldSource.Cancel();
oldSource.Dispose();
return newSource.Token;
}
public void Dispose()
{
_disposed = true;
_semaphore.Dispose();
_cts.Dispose();
}
}
这是一个重新创建测试示例的小测试程序
internal class Program
{
static LastInLocker locker = new LastInLocker();
private static void Main(string[] args)
{
Task.Run(() => Test("A"));
Thread.Sleep(500);
Task.Run(() => Test("B"));
Thread.Sleep(500);
Task.Run(() => Test("C"));
Console.ReadLine();
}
private static void Test(string name)
{
Console.WriteLine("{0} waits for lock", name);
try
{
locker.Wait();
Console.WriteLine("{0} acquires lock", name);
Thread.Sleep(4000);
locker.Release();
Console.WriteLine("{0} releases lock", name);
}
catch (Exception)
{
Console.WriteLine("{0} fails to acquire lock", name);
}
}
}
输出
A waits for lockA acquires lockB waits for lockC waits for lockB fails to acquire lockA releases lockC acquires lockC releases lock
关于c# - 只释放给后进线程的线程同步(加锁),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32361372/
确保只有“后入”线程可以访问互斥锁/锁定区域而中间线程不获取锁的正确方法是什么? 示例序列: A acquires lock B waits C waits B fails to acquire lo
synchronized 是 Java 语言中处理并发问题的一种常用手段,它也被我们亲切的称之为“Java 内置锁”,由此可见其地位之高。然而 synchronized 却有着多种用法,当它修饰
某个线程要共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进入写入操作,
我是一名优秀的程序员,十分优秀!