Декларативная безопасность потоков в .NET - PullRequest
4 голосов
/ 04 февраля 2009

Мне нужно сделать существующий поток приложений безопасным. Из-за обстоятельств (см. Ниже) я решил использовать один единственный 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. Это означает, что ВСЕ клиенты фактически читают / пишут одни и те же бизнес-объекты. Это нельзя изменить, оно высечено в камне. Надежда на безопасность потоков заключается в том, что эти удаленные бизнес-объекты доступны только для чтения в глазах удаленных клиентов (хотя они и делают много циклов), а все операции записи красиво разделены на выделенный фасад. Я ожидаю редких записей и частых чтений. Бизнес-объекты тесно связаны, они очень "графичны".

Ответы [ 3 ]

6 голосов
/ 04 февраля 2009

Я бы сделал что-то подобное, используя 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.

4 голосов
/ 06 февраля 2009

Прежде всего, спасибо Эндрю, указывая мне на 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.

Кроме того, как было отмечено Эндрю, Microsoft рекомендует использовать ReaderWriterLockSlim, если вы используете .NET3.5 или выше.

1 голос
/ 04 февраля 2009

Использование шага после сборки может быть хорошим решением. В чистом коде я бы создал методы расширения для ReaderWriterLock следующим образом:

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

И один для записи. Если блокировка действительно глобальная, вы даже можете создавать статические методы. Тогда ваш код выглядит намного менее раздражающим:

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

Преимущество этого метода в том, что он мелкозернистый, поэтому части метода, которые не нуждаются в синхронизации, не будут синхронизированы. Но это все еще требует внесения изменений в каждый метод. К счастью, они относительно небольшие, и весь код блокировки, который вы в конечном итоге измените, находится по крайней мере в одном месте.

...