.NET 中的声明式线程安全

发布于 2024-07-13 00:07:23 字数 1211 浏览 9 评论 0原文

我需要使现有应用程序线程安全。 由于情况(见下文),我决定对整个业务对象图使用一个 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。 这意味着所有客户端实际上读取/写入完全相同的业务对象。 这是无法改变的,是刻在石头上的。 线程安全的希望是,这些远程业务对象在远程客户端的眼中是只读的(认为它们确实循环许多列表),并且所有写入操作都很好地分离到专用的外观中。 我期望很少写入和频繁读取。 业务对象高度关联,非常“图形化”。

I need to make an existing app thread safe. Due circumstances (see below), I decided to use one single ReaderWriterLock for the entire graph of business objects. All methods/properties must look like these:

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();
   }

}

But I have immense amount of methods to cover and I am freaked out from the idea of copy/pasting.
Inspired by MethodImplAttribute, I would prefer to have a code like this while behave as the code above:

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

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

Is there a way to interrupt Thread execution before/after entering into a property or a method and adding the thread-safety precautions? Or somehow utilizing the functional language features of C#, embedding the productive body of the methods into a generic ReaderWriterLock aquiring "frame"?

A bit of background:

I am working on a project where data carrier business objects are exposed via .NET Remoting. However, these data classes are not serializable but MarshalByRef-s. That means that ALL clients actually read/write the very same business objects. This cannot be changed, it is carved in stone. The hope for thread-safety is that these remoted business objects are read-only in the eyes of the remoting clients (thought they do loop many lists) and all write operations are nicely separated into a dedicated facade.
I expect rare writes and frequent reads. The business objects are highly connected, they are very "graphy".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦在夏天 2024-07-20 00:07:23

我会使用 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;
      }
  } 
}

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

I would do something like that using PostSharp.

It runs as an extra build step and inserts the corresponding MSIL, but it will do what you want.
The PostSharp attribute definitions would look something like this (depending on your exact implementation). These could then be used as you describe above.

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;
      }
  } 
}

EDIT: Updated to address concerns. (Untested ;) )
Also, note as I said in the comment to the question, Microsoft recommends using ReaderWriterLockSlim in preference to ReaderWriterLock.

你げ笑在眉眼 2024-07-20 00:07:23

首先,感谢 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 或更高版本,微软建议使用 ReaderWriterLockSlim。

First of all, thanx to Andrew pointing me to PostSharp. Based on his answer, I ended up with this final one.

[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);
    }
}

It does exactly what I expressed in the question and is perfectly debuggable. We can make it even more useful, if we declare the attribute for an entire assembly:

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

This way we do not have to decorate every method/property, but placing this attribute in the assembly once, we braket all our methods/properties with ReaderWriterLock.

Also, as it has been pointed out by Andrew, Microsoft recommends to use ReaderWriterLockSlim if you use .NET3.5 or above.

白色秋天 2024-07-20 00:07:23

使用构建后步骤可能是一个很好的解决方案。 在纯代码中,我将在 ReaderWriterLock 上创建扩展方法,如下所示:

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

其中一个用于写入。 如果锁确实是全局的,您甚至可以创建静态方法。 那么你的代码看起来就不那么烦人了:

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

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

Using a post-build step might be a good solution. In pure code, I would create extension methods on ReaderWriterLock like this:

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

And one for write. If the lock is truely global, you could even make static methods. Then your code looks a lot less annoying:

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

This has the benefit of being fine-grained, so parts of a method that don't need sync'd won't be sync'd. But, it still does require making changes to each method. Fortunately they're relatively small, and all the locking code you'd end up changing is at least in one place.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文