C# 中是否存在 XOR 样式或多类型锁?
Is there a XOR style or multi type lock in C#?
我需要一把锁,它能以不同于平常的方式实现排除。我需要一个多类型锁,而不是 Reader 写入器锁。
所以A类的人都可以加入,B类的人都可以加入,但是A和B不能一起加入。
如果这不存在,我该如何以简单而优雅的方式实现它?
如果您只是寻找排除,而不需要确保指令顺序和特定的读写内存屏障。那我举个世界上最绕的例子。
基本上它只是一种封装的方式,通过 TaskCompletionSource
到 运行 排他性地由 type 排队异步工作负载。相当 light-weight,它将与线程池配合使用并适用于多种类型。
鉴于 类
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 Class
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();
}
}
}
}
注意:这只不过是您如何解决此类问题的一个简单示例。还有许多其他方法,这并不意味着要成为同行评审代码或完美或 lock-free 代码的堡垒。这只经过了最低限度的测试,缺乏很多容错能力,以及取消令牌和完整性检查等基本功能。
我需要一把锁,它能以不同于平常的方式实现排除。我需要一个多类型锁,而不是 Reader 写入器锁。
所以A类的人都可以加入,B类的人都可以加入,但是A和B不能一起加入。
如果这不存在,我该如何以简单而优雅的方式实现它?
如果您只是寻找排除,而不需要确保指令顺序和特定的读写内存屏障。那我举个世界上最绕的例子。
基本上它只是一种封装的方式,通过 TaskCompletionSource
到 运行 排他性地由 type 排队异步工作负载。相当 light-weight,它将与线程池配合使用并适用于多种类型。
鉴于 类
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 Class
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();
}
}
}
}
注意:这只不过是您如何解决此类问题的一个简单示例。还有许多其他方法,这并不意味着要成为同行评审代码或完美或 lock-free 代码的堡垒。这只经过了最低限度的测试,缺乏很多容错能力,以及取消令牌和完整性检查等基本功能。