В чем разница между атомарными и неатомарными атрибутами? - PullRequest
1796 голосов
/ 26 февраля 2009

Что означают atomic и nonatomic в объявлениях свойств?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

Какая оперативная разница между этими тремя?

Ответы [ 27 ]

1724 голосов
/ 26 февраля 2009

последние два идентичны; «atomic» - поведение по умолчанию ( обратите внимание, что на самом деле это не ключевое слово; оно определяется только отсутствием nonatomic - atomic, добавленного в качестве ключевого слова в последних версиях llvm / лязг).

Предполагая, что вы @ynthesizing реализации метода, атомарный против неатомарного изменяет сгенерированный код. Если вы пишете свой собственный установщик / получатель, атомарный / неатомный / сохранить / назначить / копировать просто рекомендации. (Примечание: @synthesize теперь является поведением по умолчанию в последних версиях LLVM. Также нет необходимости объявлять переменные экземпляра; они также будут автоматически синтезироваться, и к их имени будет добавлен _, чтобы предотвратить случайный прямой доступ ).

С помощью «atomic» синтезированный установщик / получатель будет гарантировать, что значение целом всегда возвращается из получателя или устанавливается установщиком, независимо от активности установщика в любом другом потоке. То есть, если поток A находится в середине метода получения, а поток B вызывает метод установки, фактическое жизнеспособное значение - объект с автоматически освобожденным кодом, скорее всего, - будет возвращено вызывающей стороне в A.

В nonatomic такие гарантии не предоставляются. Таким образом, nonatomic значительно быстрее, чем «атомарный».

То, что «атомарный» делает , а не , - это предоставление каких-либо гарантий безопасности потоков. Если поток A вызывает метод получения одновременно, а поток B и C вызывают метод установки с различными значениями, поток A может получить любое одно из трех возвращенных значений - одно до вызова любого метода установки или любое из значений, переданных в методы установки в B и C. Аналогичным образом, объект может в конечном итоге получить значение из B или C, что никак нельзя сказать.

Обеспечение целостности данных - одна из основных задач многопоточного программирования - достигается другими средствами.

Добавление к этому:

atomicity одного свойства также не может гарантировать безопасность потока, когда в игре задействовано несколько зависимых свойств.

Рассмотрим:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

В этом случае поток A мог бы переименовать объект, вызвав setFirstName:, а затем вызвав setLastName:. Между тем, поток B может вызвать fullName между двумя вызовами потока A и получит новое имя вместе со старой фамилией.

Чтобы решить эту проблему, вам нужна транзакционная модель . То есть некоторый другой вид синхронизации и / или исключения, который позволяет исключить доступ к fullName во время обновления зависимых свойств.

351 голосов
/ 26 февраля 2009

Это объясняется в документации Apple , но ниже приведены некоторые примеры того, что на самом деле происходит. Обратите внимание, что ключевое слово «atomic» отсутствует, если вы не укажете «nonatomic», тогда свойство является атомарным, но явное указание «atomic» приведет к ошибке.

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

Теперь атомарный вариант немного сложнее:

//@property(retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

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

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

160 голосов
/ 25 мая 2012

Атомный

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

неатомарный

  • НЕ является поведением по умолчанию
  • быстрее (для синтезированного кода, то есть для переменных, созданных с использованием @property и @synthesize)
  • не потокобезопасен
  • может привести к неожиданному поведению, когда два разных процесса одновременно обращаются к одной и той же переменной
131 голосов
/ 31 января 2012

Лучший способ понять разницу - использовать следующий пример.

Предположим, что есть атомарное строковое свойство с именем "name", и если вы вызываете [self setName:@"A"] из потока A, вызываете [self setName:@"B"] из потока B и вызываете [self name] из потока C, тогда все операции в разных потоках будут выполняется последовательно, что означает, что если один поток выполняет установщик или получатель, тогда другие потоки будут ждать.

Это делает свойство "name" доступным для чтения / записи, но если другой поток D одновременно вызывает [name release], то эта операция может привести к сбою, так как здесь нет вызова setter / getter. Это означает, что объект является безопасным для чтения / записи (ATOMIC), но не потокобезопасным, поскольку другие потоки могут одновременно отправлять объекту сообщения любого типа. Разработчик должен обеспечить безопасность потоков для таких объектов.

Если свойство "name" было неатомичным, то все потоки в приведенном выше примере - A, B, C и D будут выполняться одновременно, что приведет к непредсказуемому результату. В случае атомарного, один из A, B или C будет выполняться первым, но D все еще может выполняться параллельно.

111 голосов
/ 18 августа 2012

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

Какая функциональная разница между этими 3?

Я всегда считал атомарным по умолчанию довольно любопытно. На уровне абстракции мы работаем над использованием атомарных свойств для класса в качестве транспортного средства для достижения 100% -ной безопасности потока - это угловой случай. Для действительно правильных многопоточных программ вмешательство программиста почти наверняка является требованием. Между тем, характеристики производительности и исполнения еще не были подробно описаны. Написав несколько многопоточных программ за эти годы, я все время объявлял свои свойства как nonatomic, потому что атомарное не было разумно ни для каких целей. Во время обсуждения деталей атомных и неатомных свойств на этот вопрос я при некотором профилировании столкнулся с любопытными результатами.

Исполнение

Ok. Первое, что я хотел бы прояснить, это то, что реализация блокировки определяется реализацией и абстрагируется. Луи использует @synchronized(self) в своем примере - я видел это как общий источник путаницы. Реализация на самом деле не используется @synchronized(self); он использует уровень объекта спин-блокировки . Иллюстрация Луи хороша для иллюстрации высокого уровня, использующей конструкции, с которыми мы все знакомы, но важно знать, что она не использует @synchronized(self).

Другое отличие состоит в том, что атомарные свойства будут сохранять / освобождать цикл ваших объектов в геттере.

Производительность

Вот интересная часть: производительность с использованием атомарного доступа к свойствам в неоспоримых (например, однопоточных) случаях может быть действительно очень высокой в ​​некоторых случаях. В далеко не идеальных случаях использование атомарного доступа может стоить более чем в 20 раз дороже nonatomic. В то время как случай Contested с использованием 7 потоков был в 44 раза медленнее для трехбайтовой структуры (2,2 ГГц Core i7 Quad-Core, x86_64). Трехбайтовая структура является примером очень медленного свойства.

Интересное примечание: определяемые пользователем методы доступа трехбайтовой структуры были в 52 раза быстрее, чем синтезированные атомарные методы доступа; или 84% скорости синтезированных неатомных акцессоров.

Количество объектов в оспариваемых случаях также может превышать 50 раз.

Из-за большого количества оптимизаций и вариаций в реализации очень сложно измерить реальные воздействия в этих контекстах. Вы можете часто слышать что-то вроде «Доверяйте этому, если вы не профилируете и не найдете это проблемой» Из-за уровня абстракции на самом деле довольно сложно измерить фактическое влияние. Сбор фактических затрат из профилей может занять очень много времени, а из-за абстракций довольно неточен. Кроме того, ARC против MRC может иметь большое значение.

Итак, давайте сделаем шаг назад, не , сосредоточив внимание на реализации доступа к свойствам, мы включим обычных подозреваемых, таких как objc_msgSend, и рассмотрим некоторые реальные высокоуровневые результаты для многих обращений к NSString геттер в неоспоримых случаях (значения в секундах):

  • MRC | неатомный | получаемые вручную геттеры: 2
  • MRC | неатомный | синтезированный геттер: 7
  • MRC | атомный | синтезированный геттер: 47
  • ARC | неатомный | Синтезированный геттер: 38 (примечание: ARC добавляет количество циклов отсчета здесь)
  • ARC | атомный | синтезированный геттер: 47

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

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

93 голосов
/ 10 июля 2013

Атомная = безопасность потока

Неатомный = Нет безопасности потока

Безопасность потока:

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

В нашем контексте:

Если поток изменяет значение экземпляра, измененное значение доступно для всех потоков, и только один поток может изменять значение за раз.

Где использовать atomic:

если переменная экземпляра будет доступна в многопоточной среде.

Значение atomic:

Не так быстро, как nonatomic, потому что nonatomic не требует какой-либо контрольной работы над этим из среды выполнения.

Где использовать nonatomic:

Если переменная экземпляра не будет изменена несколькими потоками, вы можете использовать ее. Это улучшает производительность.

67 голосов
/ 24 февраля 2012

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

«атомарный» означает, что его нельзя сломать. В терминах OS / программирования вызов атомарной функции - это вызов, который не может быть прерван - вся функция должна быть выполнена, а не выгружена из ЦПУ обычным переключением контекста ОС до его завершения. Просто на тот случай, если вы не знали: поскольку ЦП может делать только одну вещь за раз, ОС вращает доступ к ЦП всем запущенным процессам за небольшие промежутки времени, чтобы создать иллюзию многозадачности. , Планировщик ЦП может (и делает) прерывать процесс в любой момент его выполнения - даже в середине вызова функции. Таким образом, для таких действий, как обновление переменных общего счетчика, когда два процесса могут попытаться обновить переменную одновременно, они должны выполняться «атомарно», т. Е. Каждое действие обновления должно завершиться полностью, прежде чем любой другой процесс может быть перенесен на CPU.

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

Поскольку переменные atomic не могут быть прерваны, значение, содержащееся в них в любой точке, (thread-lock) гарантированно будет не повреждено , хотя, гарантируя, что эта блокировка потока делает доступ к ним медленнее , non-atomic переменные, с другой стороны, не дают такой гарантии, но предлагают роскошь быстрого доступа. Подводя итог, перейдите с non-atomic, когда вы знаете, что ваши переменные не будут доступны нескольким потокам одновременно, и ускорит процесс.

65 голосов
/ 21 марта 2013

После прочтения большого количества статей, публикаций Stack Overflow и создания демонстрационных приложений для проверки атрибутов переменных свойств я решил собрать всю информацию об атрибутах:

  1. atomic // По умолчанию
  2. nonatomic
  3. strong = retain // По умолчанию
  4. weak = unsafe_unretained
  5. retain
  6. assign // По умолчанию
  7. unsafe_unretained
  8. copy
  9. readonly
  10. readwrite // По умолчанию

В статье Переменные атрибуты свойств или модификаторы в iOS вы можете найти все вышеупомянутые атрибуты, и это определенно вам поможет.

  1. atomic

    • atomic означает, что только один поток обращается к переменной (статический тип).
    • atomic является поточно-ориентированным.
    • Но это медленно в работе
    • atomic - поведение по умолчанию
    • Атомарные методы доступа в среде без сбора мусора (т.е. при использовании retain / release / autorelease) будут использовать блокировку, чтобы гарантировать, что другой поток не будет мешать правильной установке / получению значения.
    • На самом деле это не ключевое слово.

    Пример:

        @property (retain) NSString *name;
    
        @synthesize name;
    
  2. nonatomic

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

    Пример:

        @property (nonatomic, retain) NSString *name;
    
        @synthesize name;
    
53 голосов
/ 26 февраля 2009

Атомный:

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

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

-(void) setName:(NSString*)string
{
  if (name)
  {
    [name release]; 
    // what happens if the second thread jumps in now !?
    // name may be deleted, but our 'name' variable is still set!
    name = nil;
  }

  ...
}

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

Минусы: Хит производительности, делает выполнение немного медленнее

Неатомный:

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

Плюсы: Чрезвычайно быстрое исполнение.

Минусы: Возможны значения мусора в случае многопоточности.

52 голосов
/ 26 февраля 2009

Самый простой ответ: нет разницы между вашими вторыми двумя примерами. По умолчанию средства доступа к свойствам являются атомарными.

Атомарные методы доступа в среде без сбора мусора (т.е. при использовании retain / release / autorelease) будут использовать блокировку, чтобы гарантировать, что другой поток не будет мешать правильной установке / получению значения.

См. Раздел " Производительность и многопоточность " документации Apple Objective-C 2.0 для получения дополнительной информации и других соображений при создании многопоточных приложений.

...