Ненавижу какао, на самом деле.

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

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

Пример: вам нужно сделать какие-нибудь более-менее долгие манипуляции с сотней объектов. Чтобы не заставлять пользователя ждать пока операция завершится — пускай себе дальше в интерфейсе копается — нам надо запустить эти манипуляции в отдельном треде. Окей, но что если у юзера многоядерный процессор? Например, четыре ядра. Чтобы ускорить манипуляции, нужно создать не один тред, скажем, три или четыре — пусть компьютер работает на полную мощность! А если у пользователя 100 ядер? Блин, придется либо забить на все это дело и сделать один-два-три-четыре треда, либо узнавать количество ядер и кодить какую-нибудь штуку, которая запускала бы нужное количество тредов.

Добро пожаловать в Cocoa! Здесь есть классы NSOperation и NSOperationQueue (начиная с Mac OS X 10.5). Делаем подкласс NSOperation, в котором описываем нужную операцию, создаем 100 экземпляров этого класса, а потом просто ставим их всех в очередь — добавляем в NSOperationQueue. Все! Дальше Cocoa при участии ядра операционки само решит сколько тредов нужно создавать, когда их запускать, и какое количество запустить одновременно, учитывая особенности компьютера и его текущую загруженность. Ахххх, какое удовольствие! (Кстати, если нужны какие-нибудь зависимости, например, одна операция не может сработать пока не закончится другая — пожалуйста, и это тоже можно).

Вот так. Cocoa FTW. Давайте, теперь оправдывайтесь.

ksavelev 2008-06-24 16:08

Для выньдевелоперов есть CCR. И скоро выйдет ParallelFX (пока beta). CCR помощнее, ParallelFX попроще. Думаю скоро вам и линуксоиды ответят.

meowth 2008-06-24 17:08

Разработчики дотнет сразу же узнали давно и горячо полюбившийсч им ThreadPool/WorkingQueue, а ожидающие сx09 оживленно заговорили про будущий релиз фьючерсов :)

meowth: Нее, это не ThreadPool.

Rafiki 2008-06-24 18:08

Не TheradPool, но BackgrowndWorker, причём немного кастрированный :)

Anton Gladchenko 2008-06-24 18:08

Cocoa - штука интересная, сейчас как раз изучаю. Непривычно немного, но крайне интересно.

Ильяс Салихов 2008-06-24 19:08

Единсвенное не пойму, почему в качестве базового языка разработки Apple выбрала ObjectiveC, противоречиво, кто-то хвалит его, кто-то нет...

Виталий 2008-06-24 19:08

pthreads + MPProcessorsScheduled + нормальная архитектура и у меня особых проблем с портированием многопоточной софтины с винды не возникло.

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

вообще, кокоа, это интересно. более того, лет так 10 назад это было вообще революционно. но сейчас все эти фичи уже появились в более "обычных" языках. а кокоа будет страдать из-за obj-c, который, все-таки, ужасен ;)

Виталий 2008-06-24 19:08

во, предлагаю тебе тему для второй части: "как легко и просто сделать своими руками NSZoomScrollView с центрированием контента, если он маленький"

в леопарде, вроде, готовый класс сделали. а в тигре мне сейчас ой как весело ;)

Rafiki: BackgroundWorker... а очередь где?

ksavelev: ParallelFX действительно интересная штука. Мне понравилось: Parallel.For(....)

Виталий: ага, чего еще написать? :)) Насчет "строго последовательно или строго в одном потоке" — см. Configuring Dependencies Among Operation Objects.

Ильяс: потому что ObjC — прелестнейший язык! Потом об этом напишу.

Виталий 2008-06-24 19:08

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

дури везде полно. я в первые три дня изучения кокоа "подвесил" тамошний прогресс-бар. в тигре. в леопарде не повторяется. механизм простой: после пары -setHidden:YES/NO он перестает обновляться, пока в него не кликнешь.

Виталий: а как насчет центрирования контента прямо в NSView, который содержится в NSScrollView?

Виталий: конечно, багов везде полно :)

Насчет таймеров — нахрен они вообще нужны? :)

setHidden? А как насчет removeFromSuperview? :)

Виталий 2008-06-24 19:08

т.е. NSView полезет через NSClipView, чтобы узнать -boundSize у NSScrollView и поресайзить себя под этот размер? как-то неизящно для кокоа ;)

я нашел в инете решение через сабкласс NSClipView и ручное создание всей этой троицы, в обход IB. некузяво.

я нашел в инете решение через сабкласс NSClipView и ручное создание всей этой троицы, в обход IB. некузяво.

Так это как раз отлично. Так и надо )

А зачем ручное создание, кстати? :)

Виталий 2008-06-24 19:08

removeFromSuperView? надо глянуть. не видел. а обратно я его поставить смогу потом? рект запоминать придется?

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

Виталий 2008-06-24 19:08

зачем ручное - хз. я в IB не нашел куда ткнуть, чтобы после заворачивания вьюхи в скролл, достучаться до клипа.

эту проблему оставил завтра на утро. пока у меня контент отцентрирован по левому нижнему краю (кстати, перевернутая ось OY - тема для четвертого поста, я считаю ;).

Я как раз эти setHidden не использовал (потому что они себя ведут не так, как я ожидал), всегда удалял/добавлял в superview. Обратно можно, только не забыть retain. Насчет ректов не помню.

Таймеры всегда ненавидел. Пусть лучше потоки шлют нотификации чере NSDistributedNotificationCenter. А зачем не сразу запускать?

Виталий 2008-06-24 20:08

ну если есть контрол, который надо показывать изредка, то что использовать, как не -setHidden, как считаешь? ;)

зачем не сразу? представь, что есть два метода отрисовки картинки на вьюхе: быстрый и некачественный и медленный и качественный. юзер мышкой подвигал, поменял что-то, вьюха перерисовалась быстро, но некачественно. в этот момент начинается отсчет, скажем, 5-ти секунд. если юзверь за это время решает еще что-то поменять, то 5 секунд начинают отсчитываться заново. а если он ничего уже не делает, то через 5 секунд запускается более "тяжелый" алгоритм отрисовки (тут как раз прогресс появлялся, который не полз ;)

А если view mode в IB поменять на дерево, там оно есть?

Перевернутая ось — ага, совсем непривычно поначалу.

Виталий 2008-06-24 20:08

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

Виталий 2008-06-24 20:08

поначалу... да она по-жизни непривычна.

почему во вьюхе ось вверх, а в картинке - вниз? ;))

а мне рамку Crop'а нужно сделать для картинки. так весело переводить координаты туда-сюда, блин.

думал -isFlipped поможет - хрен там. картинка перевернулась, мать ее ;)

зачем не сразу? представь, что есть два метода отрисовки картинки на вьюхе

Да, makes sense.

почему во вьюхе ось вверх, а в картинке - вниз? ;))

а мне рамку Crop'а нужно сделать для картинки. так весело переводить координаты туда-сюда, блин.

думал -isFlipped поможет - хрен там. картинка перевернулась, мать ее ;)

А если setFlipped:YES на картинке и на view? :)

А картинки, кстати, не все с верхнего левого угла "начинаются", на сколько помню :) convertRect:fromView: на них!

И все-таки, про скроллвью. Ты же ведь ему даешь NSView свой с картинкой? Если она маленькая, почему бы в этом NSView и не отцентрировать?

Fan 2008-06-24 21:08

не дал я ладу с этой Cocoa... Увы.

Виталий 2008-06-25 00:08

есть потомок NSView, который делает с картинкой то, что мне надо. у него есть метод

- (void) setImage: (NSImage *) img {...}

в нем я retain'ю картинку и максимум, что могу сделать, это вызвать себе же -setFrame: NSMakeFrame(0, 0, w, h) по размерам этой картинки. это я и делаю.

чтобы мелкую картинку центрировать, надо знать размер видимой области скроллвью, это двумя уровнями выше. мне кажется уродливым лезть из класса так "высоко".

Виталий 2008-06-25 00:08

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

Виталий:

Нормально,

NSView:

enclosingScrollView

Returns the nearest ancestor NSScrollView object containing the receiver (not including the receiver itself); otherwise returns nil.

- (NSScrollView *)enclosingScrollView

А можно и не лезть, а получать NSNotification о том, что картинку/скроллвью ресайзнули.

Виталий, сейчас писал код с модальным sheet и таймером. Таймер сработал :) У тебя таймер случайно не в run loop другого треда добавляется?