2013-06-07 2 views
4

Я написал статический класс, который является хранилищем некоторых функций, которые я вызываю из другого класса.Как сделать поток статического метода безопасным?

public static class CommonStructures 
{ 
    public struct SendMailParameters 
    { 
     public string To { get; set; } 
     public string From { get; set; } 
     public string Subject { get; set; } 
     public string Body { get; set; } 
     public string Attachment { get; set; } 
    } 
} 

public static class CommonFunctions 
{ 
    private static readonly object LockObj = new object(); 
    public static bool SendMail(SendMailParameters sendMailParam) 
    { 
     lock (LockObj) 
     { 
      try 
      { 
       //send mail 
       return true; 
      } 
      catch (Exception ex) 
      { 
       //some exception handling 
       return false; 
      } 
     } 
    } 

    private static readonly object LockObjCommonFunction2 = new object(); 
    public static int CommonFunction2(int i) 
    { 
     lock (LockObjCommonFunction2) 
     { 
      int returnValue = 0; 
      try 
      { 
       //send operation 
       return returnValue; 
      } 
      catch (Exception ex) 
      { 
       //some exception handling 
       return returnValue; 
      } 
     } 
    } 
} 

Вопрос 1: Для моего второго метода CommonFunction2, я использую новый статический замок, т.е. LockObjCommonFunction2 в этом примере, или я могу использовать тот же объект блокировки LockObj, определенный в начале функции.

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

Quesiton 3: Есть ли какие-либо проблемы при передаче общего класса вместо struct .. в этом примере SendMailParameters (который я использую для обертывания всех параметров, вместо того, чтобы иметь несколько параметров для функции SendMail)?

С уважением, MH

+0

Вы оставили некоторые важные детали, такие как состояние, от которого зависят две функции. Может ли кто-то из них изменить состояние или зависеть от внутреннего состояния класса? –

+0

Создание всех методов потокобезопасности не гарантирует, что все приложение будет потокобезопасным. Http://ericlippert.com/2013/01/31/the-no-lock-deadlock/ – Euphoric

+0

@ Благодарим за использование этой статьи. Пункт Эрика очень хорош. – aiapatag

ответ

4

Вопрос 1: Для моего второго метода CommonFunction2, я могу использовать новый статический замок, т.е. LockObjCommonFunction2 в этом примере, или может повторно использовать тот же объект блокировки LockObj определяется в начале функции.

Если вы хотите синхронизировать эти два метода, вам необходимо использовать для них один и тот же замок. Например, если thread1 обращается к вашему методу 1, а thread2 получает доступ к вашему методу2, и вы хотите, чтобы они не могли одновременно обращаться к , как, так и используют тот же замок. Но, если вы просто хотите ограничить одновременный доступ только либо Метод1 или 2, использовать различные блокировки.

Вопрос 2: Есть ли что-нибудь, что может привести к потоковому подключению? вопросов или я могу улучшить код, чтобы быть безопасной нитью.

Всегда помните, что общие ресурсы (например. Статические переменные, файлы) не поточно-, так как они легко доступны для всех потоков, таким образом, вы должны применить любой вид синхронизации (через замки, сигналы, мьютекс, и т.д).

Quesiton 3: Могут ли быть какие-либо проблемы в прохождении общего класса вместо структуры .. В этом примере SendMailParameters (который я использую оберточной всех параметров, вместо того, чтобы несколько параметров функция SendMail)?

До тех пор, пока вы применяете надлежащую синхронизацию, это будет поточно-безопасным. Для структур смотрите ссылку this.

Суть в том, что вам необходимо применить правильные синхронизационные операции для всего, что содержится в общей памяти. Также вы должны всегда учитывать область, в которой вы создаете нить, и состояние переменных, используемых каждым методом. Изменяют ли они состояние или просто зависят от внутреннего состояния переменной? Всегда ли этот поток создает объект, хотя он статический/общий?Если да, то он должен быть потокобезопасным. В противном случае, если он просто повторно использует определенный общий ресурс, тогда вы должны применить надлежащую синхронизацию. И больше всего, даже без общего ресурса, тупики все еще могут произойти, поэтому remember the basic rules in C# to avoid deadlocks. P.S. благодаря Euphoric для публикации статьи Эрика Липперта.

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

static readonly object _lock = new object(); 
    static SomeClass sc = new SomeClass(); 
    static void workerMethod() 
    { 
     //assuming this method is called by multiple threads 

     longProcessingMethod(); 

     modifySharedResource(sc); 
    } 

    static void modifySharedResource(SomeClass sc) 
    { 
     //do something 
     lock (_lock) 
     { 
      //where sc is modified 
     } 
    } 

    static void longProcessingMethod() 
    { 
     //a long process 
    } 
+1

спасибо aiapatag ... очень информативно. – user1961100

+0

@ пользователь1961100 без проблем. рад, я помог. также посмотрите на запись Эрика Липперта о блокирующих блокировках (http://ericlippert.com/2013/01/31/the-no-lock-deadlock). он не связан с каким-либо общим ресурсом, но все же он вызывает тупик. это довольно интересно. – aiapatag

0

Вы можете использовать тот же объект стопорное столько раз, сколько вам угодно, но это означает, что ни одна из областей кода в окружении того же замка можно получить доступ в то же время, различные потоки. Поэтому вам нужно планировать соответственно и тщательно.

Иногда лучше использовать один объект блокировки для множественного расположения, если есть несколько функций, которые редактируют один и тот же массив, например. В других случаях более одного объекта блокировки лучше, потому что даже если один раздел кода заблокирован, другой может работать.

Многопоточное кодирование все о тщательном планировании ...

Чтобы быть супер пупер безопасным, за счет потенциально писать гораздо медленнее кода ... можно добавить аксессор в свой статический класс окружать замок. Таким образом, вы можете убедиться, что ни один из методов этого класса никогда не будет вызван двумя потоками одновременно. Это довольно грубая сила, и определенно «нет-нет» для профессионалов. Но если вы просто знакомы с тем, как это работает, это не плохое место для начала обучения.

+1

Вы могли бы предоставить фрагмент этой идеи, который у вас есть с этим аксессуаром, потому что я не совсем понял это? – Rafal

+0

Что такое аксессор к статическому __class__? –

+0

@ Rafal Я предполагаю, что я должен уточнить ... на самом деле это будет статический аксессуар для класса (т. Е. Создание «singleton»). Что-то вроде: 'public MyClass & INSTANCE() {static MyClass iInstance; возвращение iInstance; } 'и использовать это для доступа к интерфейсам класса. – Mattingly

0

1) Как первое, это зависит от того, что вы хотите иметь:

Как (два отдельных замок объекты) - никакие две нитей не исполнят тот же метод, в то же время, но они могут выполнять различные методы в в то же время.

Если вы изменили свой статус на один объект блокировки, то ни один из двух потоков не выполнит эти разделы под общим объектом блокировки.

2) В вашем фрагменте нет ничего, что меня поразило бы, но кода не так много. Если ваш репозиторий вызывает методы от себя, тогда у вас может возникнуть проблема, и есть мир проблем, с которыми вы можете столкнуться :)

3) Что касается структур, я бы их не использовал. Использовать классы лучше/проще, так как есть еще один мешок проблем, связанных с структурами, вам просто не нужны эти проблемы.

+0

Спасибо Rafal ... Я использовал struct, потому что он просто передавал значения параметров и не имеет никакой операции как таковой. Можете ли вы рассказать о каких проблемах со структурой, о которых вы говорили? Кроме того ... вы упомянули в пунктах 2, методы вызова репозитория сами по себе вызовут проблему .. снова по достоинству оценят некоторую ясность вокруг этого, пожалуйста .. – user1961100

+0

Что касается структур, там есть некоторые статьи и даже SO [вопросы] (http: // stackoverflow.com/questions/521298/when-to-use-struct-in-c), посвященный им. Если вы спросите меня, что вам не нужна структура в 99% случаев. Что касается проблем с потоками, я имел в виду простой тупик, когда по 1-му потоку один метод (lock1) вызывает другой (lock2), а во 2-м потоке порядок отменяется. – Rafal

0

Количество используемых объектов блокировки зависит от того, какие данные вы пытаетесь защитить. Если у вас есть несколько переменных, которые читаются/обновляются для нескольких потоков, для каждой независимой переменной вы должны использовать отдельный объект блокировки. Поэтому, если у вас есть 10 переменных, которые образуют 6 независимых групп переменных (насколько вы намерены их читать и записывать), вы должны использовать 6 объектов блокировки для лучшей производительности. (Независимая переменная - это та, которая читается/записывается по нескольким потокам, не влияя на значение других переменных. Если 2 переменные должны быть прочитаны вместе для данного действия, они зависят друг от друга, поэтому они должны быть заблокированы вместе.Надеюсь, это не слишком запутывает.)

Заблокированные регионы должны быть как можно короче для максимальной производительности - каждый раз, когда вы блокируете область кода, ни одна другая нить не может войти в эту область до тех пор, пока блокировка не будет отпущена. Если у вас есть несколько независимых переменных, но используйте слишком мало объектов блокировки, ваша производительность будет страдать, потому что ваши заблокированные регионы будут расти дольше.

Наличие большего количества объектов блокировки допускает более высокий параллелизм, поскольку каждый поток может читать/записывать другую независимую переменную - потоки должны только ждать друг друга, если они пытаются читать/записывать переменные, зависящие друг от друга (и, следовательно, блокируются через один и тот же объект блокировки).

В вашем коде вы должны быть осторожны с вашим входным параметром SendMailParameters - если это ссылочный тип (класс, а не структура), вы должны убедиться, что его свойства заблокированы или к ним не доступны несколько потоков. Если это ссылочный тип, это просто указатель и без блокировки внутри его свойств getters/seters, несколько потоков могут попытаться прочитать/записать некоторые свойства одного и того же экземпляра. Если это произойдет, ваша функция SendMail() может закончиться использованием поврежденного экземпляра. Недостаточно просто иметь замок внутри SendMail() - свойства и методы SendMailParameters также должны быть защищены.

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