4

Мне нужно сделать существующий поток приложений безопасным. Учитывая обстоятельства (см. Ниже), я решил использовать один единственный ReaderWriterLock для всего графика бизнес-объектов. Все методы/свойства должны выглядеть эти:Декларативная безопасность потоков в .NET

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

+0

черт возьми, вы должны действительно получить новую работу =) ... –

+0

Обратите внимание, что MSDN (http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlock.aspx) рекомендует использовать ReaderWriterLockSlim вместо ReaderWriterLock. –

ответ

4

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

Таким образом, мы не должны украшать каждый метод/свойство, но размещение этого атрибута в сборке один раз, мы Braket все наши методы/свойства с помощью ReaderWriterLock.

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

+0

Вы должны были принять фактический ответ, который помог вам, а не вашим собственным. – dpan

+0

Нет! Я должен принять правильный ответ, чтобы помочь другим читателям! Я отдал должное Андрею за его очень ценный вклад. (Кстати, приняв мой собственный ответ, я не получаю очки вознаграждения) –

+1

Мех. Я не в нем для очков :) –

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

EDIT: Обновлено для решения проблем. (Untested;)) Кроме того, обратите внимание, как я сказал в комментарии к вопросу, Microsoft recommends, используя ReaderWriterLockSlim, предпочтительно ReaderWriterLock.

+0

именно то, что я сделал бы. :) +1 –

+0

Почему, спасибо :) –

+0

Хотя это мой любимый ответ, он недостаточно хорош, из-за небольшой, но критической детали. Переопределение OnEntry вызывает захват блокировки, идущей внутри раздела «try». Если обнаружение блокировки завершится неудачей (и выкинет исключение), раздел «finally» все равно будет работать. –

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

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

Смежные вопросы