c# - C# 中是否有 XOR 样式或多类型锁?
问题描述
我需要一个以与平常不同的方式实现排除的锁。我需要一个多类型锁,而不是 Reader Writer 锁。
所以A型的每个人都可以加入,B型的每个人都可以加入,但A和B不能一起加入。
如果这不存在,我该如何以简单优雅的方式实现它?
解决方案
如果您只是在寻找排除项,并且不需要确保指令顺序和特定的读写内存屏障。然后我举一个世界上最复杂的例子。
基本上,它只是一种通过 a 将异步工作负载排队TaskCompletionSource
以仅按type运行的封装方式。它相当轻量级,可以很好地与线程池配合使用并适用于多种类型。
鉴于类
public class Base
{
public int Id { get; set; }
}
public class Bob:Base { }
public class Cat:Base { }
public class Flib:Base { }
某种工作量
public static async Task SharedAsync<T>(T instance)
where T : Base
{
await _xor.WaitAsync(instance);
try
{
Console.WriteLine("Starting " + typeof(T) + " " + instance.Id);
await Task.Delay(100);
}
finally
{
Console.WriteLine("Fishing " + typeof(T) + " " + instance.Id);
_xor.Release(instance);
}
}
用法
_xor = new XorLimiter(typeof(Bob), typeof(Cat), typeof(Flib));
static Task Generate(int x)
=> _r.Next(0, 3) switch
{
0 => SharedAsync(new Bob() {Id = x}),
1 => SharedAsync(new Cat() {Id = x}),
2 => SharedAsync(new Flib() {Id = x}),
_ => throw new ArgumentOutOfRangeException()
};
var tasks = Enumerable
.Range(0, 15)
.Select(Generate);
await Task.WhenAll(tasks);
Console.WriteLine("Finished");
输出
Starting Program+Bob 0
Starting Program+Bob 3
Starting Program+Bob 7
Starting Program+Bob 8
Starting Program+Bob 9
Starting Program+Bob 14
Fishing Program+Bob 9
Fishing Program+Bob 7
Fishing Program+Bob 8
Fishing Program+Bob 14
Fishing Program+Bob 3
Fishing Program+Bob 0
Starting Program+Flib 1
Starting Program+Flib 2
Starting Program+Flib 5
Starting Program+Flib 10
Fishing Program+Flib 10
Fishing Program+Flib 5
Fishing Program+Flib 1
Fishing Program+Flib 2
Starting Program+Cat 4
Starting Program+Cat 6
Starting Program+Cat 11
Starting Program+Cat 12
Starting Program+Cat 13
Fishing Program+Cat 12
Fishing Program+Cat 13
Fishing Program+Cat 6
Fishing Program+Cat 11
Fishing Program+Cat 4
Finished
XorLimiter 类
public class XorLimiter
{
private readonly Type[] _types;
private readonly object _sync = new object();
private Type _current;
private readonly Queue<Type> _next = new Queue<Type>();
private readonly Dictionary<object, int> _processing = new Dictionary<object, int>();
private readonly Dictionary<Type,List<(object instance, TaskCompletionSource<bool> source)>> _waiting = new Dictionary<Type, List<(object instance, TaskCompletionSource<bool> source)>>();
public XorLimiter(params Type[] types) => _types = types;
private void Process()
{
if (_processing.Any() || !_next.Any()) return;
_current = _next.Dequeue();
if(!_waiting.TryGetValue(_current,out var list))
throw new InvalidOperationException("Nothing to process");
foreach (var (instance, source) in list)
{
AddOrUpdate(instance);
source.TrySetResult(true);
}
_waiting.Remove(_current);
}
public void Release<T>(T instance)
{
if(!_types.Contains(typeof(T)))
throw new InvalidOperationException("Not monitoring type : " + typeof(T).Namespace);
lock (_sync)
{
try
{
if (!_processing.ContainsKey(instance))
throw new InvalidOperationException("Instance does not exists");
_processing[instance]--;
if (_processing[instance] < 0)
throw new InvalidOperationException("Instance count is less than 0");
if (_processing[instance] == 0)
_processing.Remove(instance);
}
finally
{
Process();
}
}
}
private void AddOrUpdate<T>(T instance)
{
if (_processing.ContainsKey(instance))
_processing[instance]++;
else
_processing.Add(instance,1);
}
public ValueTask WaitAsync<T>(T instance)
{
if(!_types.Contains(typeof(T)))
throw new InvalidOperationException("Not monitoring type : " + typeof(T).Namespace);
lock (_sync)
{
try
{
_current ??= typeof(T);
if (_current ==typeof(T))
{
AddOrUpdate(instance);
return new ValueTask();
}
var tcs = new TaskCompletionSource<bool>();
if (!_waiting.TryGetValue(typeof(T), out var list))
{
_waiting.Add(typeof(T), list = new List<(object instance, TaskCompletionSource<bool> source)>());
_next.Enqueue(typeof(T));
}
list.Add((instance,tcs));
return new ValueTask(tcs.Task);
}
finally
{
Process();
}
}
}
}
注意:这只是一个关于如何解决此类问题的简单示例。还有许多其他方法,这并不意味着是同行评审代码或完美或无锁代码的堡垒。这仅经过最低限度的测试,缺乏很多容错能力,以及取消令牌和神圣检查等基本功能。
推荐阅读
- javascript - 选择 SlickGrid 的第一行
- .net - Ruby - 无法打开 STDIN 管道到 .NET 应用程序
- apache-kafka - 卡夫卡到雪花连接问题
- terraform - 在资源级别使用 for / for_each 迭代嵌套数据
- arrays - Class-validator - 验证对象数组
- python - 使用 Xception CNN 训练自定义图像时出现奇怪的错误
- mongodb - MongoDB:由于内部错误而失去空运行选举
- python - Pandas DateTimeIndex ceil 模棱两可的论点
- java - Activiti createTaskQuery().processInstanceId 返回空
- android - 无法使用 Andriod 本机 sipstack 从 Android 设备发送或跟踪 Dtmf 到服务器?