4

既存のアプリをスレッドセーフにする必要があります。事情により (以下を参照)、ビジネス オブジェクトのグラフ全体に対して 1 つの 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;
      }
  } 
}

編集:懸念に対処するために更新されました。(未テスト;))また、質問へのコメントで述べたように、Microsoft.ReaderWriterLockSlimReaderWriterLock

于 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.*")]

このように、すべてのメソッド/プロパティを装飾する必要はありませんが、この属性をアセンブリに1回配置すると、ReaderWriterLockを使用してすべてのメソッド/プロパティにブレーキをかけます。

また、Andrewが指摘しているように、.NET3.5以降を使用している場合は、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(); }
}

そして1つは書き込み用です。ロックが本当にグローバルである場合は、静的メソッドを作成することもできます。そうすれば、コードの煩わしさが大幅に軽減されます。

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

これにはきめ細かい利点があるため、同期を必要としないメソッドの部分は同期されません。ただし、それでも各メソッドに変更を加える必要があります。幸いなことに、それらは比較的小さく、変更することになったすべてのロックコードは少なくとも1か所にあります。

于 2009-02-04T19:40:44.237 に答える