2010-04-04 6 views
1

У меня есть класс с несколькими свойствами. При каждом обновлении значения вызывается метод Store, который хранит все поля (в файле).Является ли этот код потокобезопасным?

private int _Prop1; 
public int Prop1 { 
    get { 
     return _Prop1; 
    } 
    set { 
     _Prop1 = value; 
     Store(); 
    } 
} 

// more similar properties here... 

private XmlSerializer _Ser = new ...; 
private void Store() 
{ 
    lock (_Ser) { 
     using (FileStream fs = new ...) { 
      _Ser.Serialize (fs, this); 
     } 
    } 
} 

Этот дизайн нить-сейф?

(Кстати, если вы можете думать о более соответствующей подписи, не стесняйтесь редактировать).

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

Уточнение: Свойства не будут устанавливаться очень часто, но они могут быть установлены одновременно. Важно иметь действительный файл большую часть времени.

Если поток изменит свойство относительно значения свойства, он должен заблокировать весь объект для синхронизации с другими потоками. Это в основном то же самое, что и при блокировке на List при перечислении и не несет ответственности за этот класс.

+0

Сохранение изменений на диске после каждой модификации свойств звучит ужасно неэффективно. Что делать, если кто-то хочет сразу изменить 20 свойств? –

+0

Это внутренний компонент, поэтому я знаю, что он не будет изменяться очень часто. – mafu

+0

«в конце концов, каждое свойство будет иметь свое последнее значение»; возможно ли что-нибудь прочитать файл между этими магазинами, оставив значение чтения в неопределенном состоянии (например, два свойства, имеющие несовместимые значения)? То же самое может произойти, если по какой-то причине один из магазинов не работает. –

ответ

1

Недостаточно кода для совершения звонка. Но, конечно, ничего хорошего не произойдет, если вы не сериализуете доступ на запись к файлу. Второй поток, который присваивает свойство, собирается бомбить на IOException, если 1-й поток все еще занят записью файла.

Мелкозернистая блокировка, как это, как правило, является проблемой. Клиентский код может быть занят, изменяя более одного свойства класса. Вы получите частичное обновление, если возникает исключение, создавая файл, который содержит сериализованное состояние, которое является недопустимым и может вызвать проблемы при чтении. Вам понадобится нечто вроде пары BeginUpdate(), EndUpdate().

+0

Файл доступ синхронизируется в OP-коде, не так ли? – mafu

+0

О вашем втором абзаце: Это правда, но внутренне невозможно объяснить эту проблему в самом классе - если Store не отложен до тех пор, пока все значения не будут изменены. «Не знаю, как это реализовать. Кроме того, в моем случае это не обязательно :) – mafu

+1

@mafutrct: да, вы редко можете создать класс потокобезопасным без помощи кода клиента. Вот почему классы .NET Framework оставляют это полностью до клиентского кода. Неправильная блокировка, когда клиент просто никогда не использует ее в нескольких потоках, тоже невелик. Хотя здесь это не имеет особого значения, средство настройки свойств уже очень медленно. Посмотрите на класс .NET ApplicationSettings для руководства, он имеет явный метод Save(). Но он отслеживает состояние «должно сохранять». –

5

Это зависит от того, что вы звоните на разные темы.

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

+0

Да, но в этом случае они будут сериализованы снова после их изменения. – mafu

0

Я бы предложил использовать какой-то Mutex, чтобы гарантировать, что данные только сериализованы в определенной точке, это может сделать так же, как обновление изменится или вот-вот изменится.

Следующая ссылка приведет вас простой пример на веб-сайте MSDN, что следует надеяться продемонстрировать это для вас:

Mutex Example

+0

Как мне это сделать? Я не знаком с Mutex, не могли бы вы привести краткий пример с использованием кода OP? Кроме того, я чувствую, что это слишком много, если на самом деле это не нужно. – mafu

+0

Я добавил ссылку на некоторый пример кода –

1

No.

В случае, если вы хотите синхронизировать свойства theirself , этот код не является потокобезопасным, так как «блокировка» не находится в значении _Prop1, а только на _Ser. Действительно, когда поток получает свойство, свойство может быть установлено другим потоком.

Даже процесс сериализации, _Ser доступ к свойствам, которые могут быть изменены другими потоками при выполнении (в то время как _Ser работает, другой набор потоков Prop1).

Этот код фактически запрещает использование объекта XmlSerialize _Ser несколькими потоками. Если это то, что вы хотите получить ...


Ответ существенно зависит от того, что вы хотите получить.

+0

Установка базового значения свойства всегда атомарна. Если разные потоки обращаются к одному и тому же свойству, они должны синхронизировать их доступ (точно так же, как при перечислении чего-то) - это невозможно сделать в этом классе. – mafu

+0

Даже после того, как свойства были установлены несколькими потоками, они в конечном итоге будут сохранены, поэтому я не уверен, почему вы думаете, что использование XmlSerializer предотвращает это. Вы ошиблись? – mafu

+0

А, я думаю, я понял это сейчас. Вы имеете в виду, что файл может находиться в неполном состоянии в течение короткого времени, пока он не будет записан снова с последними значениями, не так ли? Это правда. В моем конкретном случае это не имеет значения, но я считаю, что это действительно проблема этого кода в общем случае. – mafu

0

Если свойство Prop1 может быть вызвано из нескольких потоков, сделайте поле _Prop1volatile.

private volatile int _Prop1; 

Из MSDN,

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

Считайте, что это хорошая практика. Это не сделает код, который вы разместили потокобезопасным, потому что, как говорили другие, в то время как класс сериализуется, значение _Prop1 может быть изменено другим потоком. Тем не менее, если у вас есть поле, которое может быть прочитано и записано несколькими потоками, маркировка поля как volatile гарантирует, что ваш код увидит самое текущее значение.

+1

Я не понимаю, как это помогает в любом случае? – mafu

+0

(после редактирования) Извините, я до сих пор этого не понимаю. Я сомневаюсь, что компилятор может оптимизировать доступ к общедоступному свойству int таким образом, который влияет на безопасность потоков. – mafu

+1

@mafutrct: Это помогает в том, что он заставляет поток чтения читать последнее значение ... или, по крайней мере, что-то в этом роде. Волатильность на самом деле скорее более тонкая, чем та. Блокировка свободной потоковой передачи с изменяемыми типами является неприятной возможностью червей :( –

0

Небезопасный поток, если тип свойства не является атомарным.

Простой примером, с нитями А и В.

A: Prop1 = foo 
A: Store() 
A: ... store saves foo.Var1 
B: Prop1 = bar 
A: ... store saves bar.Var2 (instead of foo.Var2) 

Целостность данных, возможно, нарушена. Даже с такими же простыми, как Int64, проблемы могут возникать в теории.

Помещение другого lock(_Ser) в сеттер поможет.

+0

Тип свойства является атомарным. Я бы хотел избежать 'lock (_Ser)' iff possible. В вашем примере я думаю, что это именно то, что должно произойти - значение, написанное в конце, является последним значением. – mafu

+2

Atomic! = Volatile. Просто потому, что он хранится атомарно, это не означает, что другой поток будет читать любое новое значение, которое будет храниться. –

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