2
ответа
Многопоточность
Здравствуйте!
Мой вопрос — про многопоточность, а именно:что нужно синхронизировать, а что нет.
Допустим, у нас есть объект с полем типа int, и нужно организовать доступ на чтение-запись этого поля из разных потоков. Вопросы следующие:
- Правда ли, что это поле не надо в явном виде синхронизировать для потоков, т.к. такие данные просто не возможно повредить? (Исхожу из того, что «атомами» потоков являются машинные команды, а там непосредственно присвоить что-либо переменной — это одна команда mov. Т.е. в любой момент времени в переменной лежит «целое» значение)
- Если на первый вопрос ответ утвердительный, то какие ещё типы не нуждаются в синхронизации? Видимо, все стандартные типы меньшей либо равной размерности чем int. Однако стоит вопрос о кастомных структурах, а так же о величинах типа long long int.
- Правда ли, что сгенерённое свойство для этого поля без модификатора nonatomic автоматом будет синхронизированным по объекту? (Имеется ввиду @synchronized(self)) Переформулируя вопрос: автогенерация свойств никак не зависит от типа поля, обслуживаемого свойством?
- Правда ли, что для любого стандартно-сгенерённого atomic-свойства одного класса выполняется правило, что в конкретный момент времени только оно одно из всех атомиков этого класса выполняется? А если я хочу убрать это свойство «мониторности», то мне надо заточить по замочку на каждую переменную и ручками прописать свойства?
Спасибо!
Чуть позже отвечу, там очень длинный ответ получается :)
— alex
Супер. Спасибо!
— kotenok gav
Ответы
Итак, начнем. На пальцах, более подробные объяснения в других местах.
- Во-первых, разберемся с атомарностью. Атомарность обозначает неделимость, неразрывность. То есть если у нас есть группа операций (обычно это инкремент + получение результата) — то она выполняется как единое целое. Если у нас есть два потока, которые делают эту операцию (реально — по две операции каждый), то они выполнятся строго последовательно, а не «инкремент раз — инкремент два — получение раз — получение два».
- Во-вторых, что такое синхронизация. Это когда у нас есть объект (семафор кажется), и мы говорим: «если семафор открыт — закрыть, иначе стоять тут, пока не откроют». Это обычно реализуется при помощи тех самых атомарных операций и позволяет (при удачном стечении обстоятельств) делать «атомарными» сравнительно большие блоки кода.
- «Атомарные команды» — действительно машинные. Их много разных и этот список сильно зависит от архитектуры процессора, на котором мы все это исполняем. То есть ответ на первые два вопроса: «зависит от архитектуры». Long long тоже может иметь атомарные операции. Какие являются атомарными в iPhone — не знаю.
- про nonatomic можно прочитать в документации. Если его не писать, генерится такой код (если же там nonatomic, то значение возвращается без лока. Как именно этот лок реализован — не сказано):
[_internal lock]; // lock using an object-level lock id result = [[value retain] autorelease]; [_internal unlock]; return result;
- по 4му вопросу. Этого никто не гарантирует. Atomic всего-лишь гарантирует, что в многопоточной среде ничего не сломается. То есть если А читает пропертю, а Б и В — в это же время ее записывают, то А может получить любое из трех значений.
Как-то так. Вот тут есть еще немного информации про это: stackoverflow.com/questions/588866/objective-c-properties-atomic-vs-nonatomic
- Да, тут всё понятно
- Вообще говоря, то, что Вы описали, называется spinlock-ом. Семафоры имеют некоторую «ёмкость» — количество потоков, которое можно запихнуть в охраняемую секцию. При ёмкости равной единице семафор ведёт себя так же, как и спинлок
- В первом вопросе я говорил именно про поле типа int, т.к. я откуда-то слышал, что длинна int-а равна длинне указателя, который в свою очередь является одним словом. (не получилось сходу найти этого в спецификации ANSI, нашёл только вот тут:tigcc.ticalc.org/doc/keywords.html#int) То есть на 16-битных платформах int будет 16битный, а на 32-битных — 32-битным. Таким образом этот тип «нативен» для платформы. Ну и наконец просто довод с точки зрения здравого смысла: как может попортиться int? Когда запишутс его первые два байта, второй поток успеет считать его? или что? Звучит сильно сомнительно. :)
- Комментарий «lock using an object-level lock» наводит на мысль, что там стоит синхронизация по объекту. Правда, одно я теперь знаю точно(провёл небольшой эксперимент): это скорее всего внутренний лок, и поэтому @synchronize по этому же объекту отлично выполняется вместе с доступом в свойства. Ну а больше, на самом деле, ничего не надо. Спасибо!
Про спинлоки ничего не скажу, про емкость семафоров — тоже. Там все сложно и плохо.
— alex
Про атомарность на таком уровне — ничего сказать не могу. Все-таки я обычно более высокоуровневые программы пишу. Да, про связь указателя и int говорилось, но что из этого следует — черт его знает.
— alex
@synchronize в том же потоке? Или в другом? В том же — и должно выполняться. В другом — там интереснее, если выполняется, то тогда что же ж это за лок?
— alex
@synchronize в другом потоке. Впрочем, вот код: http://paste.org/pastebin/view/18937 Скорее всего это просто приватное поле в классе NSObject, выглядит как-нибудь так: NSLock *_internal; Таким образом
Зарегистрируйтесь или
войдите, чтобы добавить свой комментарий или ответ на вопрос.
© 2009-2012, ООО «Инру»