C# 最大限度降低多线程C#代码的复杂性

同步挑战

在多线程编程方面遇到的第一个问题是,同步对共享资源的访问权限。当两个或多个线程共享对某个对象的访问权限并且可能同事尝试修改此对象时,就会出现这个问题。当C#首次发布时。lock语句实现了一种基本的方法,可确保只有一个线程能访问指定资源(如数据文件),并且效果很好。C#中的lock关键字很容易理解

不过简单的lockc存在一个主要缺陷:它不区分只读访问权限和写入访问权限。例如,可能10个不同的线程要从共享对象中读取,可以通过System.Threading命名空间下的ReaderWriterLockSlim类授权这些线程同时访问实例,而不导致问题发生。

与lock语句不同,此类可便于指定代码是降内容写入对象,还是只从对象读取内容。这样一来,多个读取器可以同时进入,但在其他所有读写线程均已完成自己的工作前,拒绝任何写入代码访问。

现在的问题是:如果使用ReaderWriterLock类,语法就会变的很麻烦,大量的重复代码即降低了可读性,又随时间变化增加了维护复杂性,并且代码中通常会分散有多个try和finally块。即使是简单的拼写错误,也可能会带来日后有时极难发现的灾难性影响。

通过将ReaderWriterLockSlim封装到简单的类中,这个问题瞬间解决,不仅重复代码不再会出现,而且还降低了拼写错误毁了一天的劳动成果的风险。图1中的类完全具于lambda技巧。可以说,这就是对一些委托应用的语法糖(假设存在几个接口)。最重要的事,它很大程度上有助于实现避免重复代码原则(DRY)

图1:封装ReaderWriterLockSlim

public class Synchronizer<TImpl, TIRead, TIWrite> where TImpl : TIWrite, TIRead
{
  ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
  TImpl _shared;

  public Synchronizer(TImpl shared)
  {
    _shared = shared;
  }

  public void Read(Action<TIRead> functor)
  {
    _lock.EnterReadLock();
    try {
      functor(_shared);
    } finally {
      _lock.ExitReadLock();
    }
  }

  public void Write(Action<TIWrite> functor)
  {
    _lock.EnterWriteLock();
    try {
      functor(_shared);
    } finally {
      _lock.ExitWriteLock();
    }
  }
}

图3创建Lambad

public void ExecuteParallel_1()
{
  var sync = new Synchronizer<string, string, string>("initial_");

  var actions = new Actions();
  actions.Add(() => sync.Assign((res) => res + "foo"));
  actions.Add(() => sync.Assign((res) => res + "bar"));

  actions.ExecuteParallel();

  string result = null;
  sync.Read(delegate (string val) { result = val; });
  Assert.AreEqual(true, "initial_foobar" == result || result == "initial_barfoo");
}

 

posted @ 2024-03-01 08:58  每天学习一点哈哈哈  阅读(16)  评论(0编辑  收藏  举报