4

我需要使现有的应用程序线程安全。由于情况(见下文),我决定为整个业务对象图使用一个 ReaderWriterLock。所有方法/属性必须如下所示:

public int MyReadOperation(string inputParam)
{
   rwLock.AcquireReaderLock(10000);
   try
   {
      // do all read operations
      ...
   }
   finally
   {
      rwLock.ReleaseReaderLock();
   }
}

public void MyWriteOperation(string input)
{
   rwLock.AcquireWriterLock(10000);
   try
   {
      // do all write operations
      ...
   }
   finally
   {
      rwLock.ReleaseWriterLock();
   }

}

但是我有大量的方法可以覆盖,我被复制/粘贴的想法吓坏了。受 MethodImplAttribute 的启发,我希望有这样的代码,而其行为与上面的代码一样:

[ReadOperation]
public int MyReadOperation(string inputParam)
{
   // do all read operations
   ...
}

[WriteOperation]    
public void MyWriteOperation(string input)
{
   // do all write operations
   ...
}

有没有办法在进入属性或方法并添加线程安全预防措施之前/之后中断线程执行?或者以某种方式利用 C# 的函数式语言特性,将方法的生产主体嵌入到获取“框架”的通用 ReaderWriterLock 中?

一点背景:

我正在开发一个通过 .NET Remoting 公开数据载体业务对象的项目。但是,这些数据类不是可序列化的,而是 MarshalByRef-s。这意味着所有客户端实际上读/写完全相同的业务对象。这是无法改变的,它是刻在石头上的。线程安全的希望是这些远程业务对象在远程客户端的眼中是只读的(认为它们确实循环了许多列表)并且所有写入操作都很好地分离到一个专用的外观中。我期待罕见的写入和频繁的读取。业务对象高度连接,它们非常“图形化”。

4

3 回答 3

6

我会使用PostSharp做类似的事情。

它作为一个额外的构建步骤运行并插入相应的 MSIL,但它会做你想做的事。PostSharp 属性定义看起来像这样(取决于您的具体实现)。然后可以按照您上面的描述使用这些。

public sealed class ReadOperationAttribute : OnMethodBoundaryAspect
{
  // not quite sure how you manage your lock, so put this dummy method in.
  ReaderWriterLock _rwLock = ReaderWriterLock.GetCorrectReaderWriterLock();

  [ThreadStatic]
  static bool _isLocked;

  public override void OnEntry( MethodExecutionEventArgs e )
  {
    try
    {
        _rwLock.AcquireReaderLock(10000);
        _isLocked = true;
    }
    catch
    {
        _isLocked = false;
        throw;
    }
  } 

  public override void OnExit( MethodExecutionEventArgs e )
  {    
      if (_isLocked) 
      {
          _rwLock.ReleaseReaderLock();
          _isLocked = false;
      }
  } 
}

public sealed class WriteOperationAttribute : OnMethodBoundaryAspect
{
  // not quite sure how you manage your lock, so put this dummy method in.
  ReaderWriterLock _rwLock = ReaderWriterLock.GetCorrectReaderWriterLock();

  [ThreadStatic]
  static bool _isLocked;

  public override void OnEntry( MethodExecutionEventArgs e )
  {
     try
    {
        _rwLock.AcquireWriterLock(10000);
        _isLocked = true;
    }
    catch
    {
        _isLocked = false;
        throw;
    }
  } 

  public override void OnExit( MethodExecutionEventArgs e )
  {
      if (_isLocked) 
      {
          _rwLock.ReleaseReaderLock();
          _isLocked = false;
      }
  } 
}

编辑:更新以解决问题。(未经测试;))另外,请注意,正如我在对问题的评论中所说,微软建议ReaderWriterLockSlim优先使用ReaderWriterLock.

于 2009-02-04T16:41:10.213 回答
5

首先,感谢 Andrew 将我指向 PostSharp。根据他的回答,我最终得到了这个。

[Serializable]
public class ReadOperationAttribute : PostSharp.Laos.OnMethodInvocationAspect
{
    public override void  OnInvocation(MethodInvocationEventArgs eventArgs)
    {
        ReaderWriterLock rwLock = GetLock();
        rwLock.AcquireReaderLock(10000);
        try { eventArgs.Proceed(); }
        finally { rwLock.ReleaseReaderLock();}
    }
}
public class Foo
{
    [ReadOperation]
    public string Bar
    {

        get { return "stuff"; }
        set { Console.WriteLine(value); }
    }

    [ReadOperation]
    public void Test(string input)
    {
        Console.WriteLine(input);
    }
}

它完全按照我在问题中表达的方式进行,并且完全可以调试。如果我们声明整个程序集的属性,我们可以使它更加有用:

[assembly: ReadOperation(AttributeTargetElements=MulticastTargets.Method,
AttributeTargetTypes="MyNamespace.*")]

这样我们就不必装饰每个方法/属性,但是将这个属性放置在程序集中一次,我们用 ReaderWriterLock 对所有方法/属性进行制动。

此外,正如 Andrew 所指出的,如果您使用 .NET3.5 或更高版本,Microsoft 建议使用 ReaderWriterLockSlim。

于 2009-02-06T13:10:05.270 回答
1

使用构建后步骤可能是一个很好的解决方案。在纯代码中,我会像这样在 ReaderWriterLock 上创建扩展方法:

public static void ReadLock(this ReaderWriterLock l, Action f) {
  l.AquireReaderLock();
  try { f(); }
  finally { l.ReleaseReaderLock(); }
}

和一个写。如果锁是真正的全局锁,你甚至可以创建静态方法。然后你的代码看起来不那么烦人了:

public void DoSomething() {
  theLock.ReadLock( () => {
  // Existing code
  });
}

这具有细粒度的好处,因此不需要同步的部分方法将不会同步。但是,它仍然需要对每种方法进行更改。幸运的是,它们相对较小,并且您最终要更改的所有锁定代码至少都在一个地方。

于 2009-02-04T19:40:44.237 回答