首页 > 解决方案 > C# 中是否有 XOR 样式或多类型锁?

问题描述

我需要一个以与平常不同的方式实现排除的锁。我需要一个多类型锁,而不是 Reader Writer 锁。

所以A型的每个人都可以加入,B型的每个人都可以加入,但A和B不能一起加入。

如果这不存在,我该如何以简单优雅的方式实现它?

标签: c#locking

解决方案


如果您只是在寻找排除项,并且不需要确保指令顺序和特定的读写内存屏障。然后我举一个世界上最复杂的例子。

基本上,它只是一种通过 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();
         }
      }
   }
}

注意:这只是一个关于如何解决此类问题的简单示例。还有许多其他方法,这并不意味着是同行评审代码或完美或无锁代码的堡垒。这仅经过最低限度的测试,缺乏很多容错能力,以及取消令牌和神圣检查等基本功能。


推荐阅读