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

Для выньдевелоперов есть CCR. И скоро выйдет ParallelFX (пока beta). CCR помощнее, ParallelFX попроще. Думаю скоро вам и линуксоиды ответят.
Разработчики дотнет сразу же узнали давно и горячо полюбившийсч им ThreadPool/WorkingQueue, а ожидающие сx09 оживленно заговорили про будущий релиз фьючерсов :)
meowth: Нее, это не ThreadPool.
Не TheradPool, но BackgrowndWorker, причём немного кастрированный :)
Cocoa - штука интересная, сейчас как раз изучаю. Непривычно немного, но крайне интересно.
Единсвенное не пойму, почему в качестве базового языка разработки Apple выбрала ObjectiveC, противоречиво, кто-то хвалит его, кто-то нет...
pthreads + MPProcessorsScheduled + нормальная архитектура и у меня особых проблем с портированием многопоточной софтины с винды не возникло.
по описанию в посте - обычный пул потоков, чесслово. ну разве что зависимостей нет. или не требовались и не нашел их. это все красиво, пока не встает дурацкой задачи обрабатывать часть элементов как-нибудь иначе. например строго последовательно или строго в одном потоке или еще как-нибудь. тогда все волшедные фреймворки приходится выкидывать и тупо кодить с нуля ;)
вообще, кокоа, это интересно. более того, лет так 10 назад это было вообще революционно. но сейчас все эти фичи уже появились в более "обычных" языках. а кокоа будет страдать из-за obj-c, который, все-таки, ужасен ;)
во, предлагаю тебе тему для второй части: "как легко и просто сделать своими руками NSZoomScrollView с центрированием контента, если он маленький"
в леопарде, вроде, готовый класс сделали. а в тигре мне сейчас ой как весело ;)
Rafiki: BackgroundWorker... а очередь где?
ksavelev: ParallelFX действительно интересная штука. Мне понравилось: Parallel.For(....)
Виталий: ага, чего еще написать? :)) Насчет "строго последовательно или строго в одном потоке" — см. Configuring Dependencies Among Operation Objects.
Ильяс: потому что ObjC — прелестнейший язык! Потом об этом напишу.
а в третьей части можешь рассказать как легко и весело всякие модальные окошки блокируют таймеры, если их не зарегистрировать в трех разных местах.
дури везде полно. я в первые три дня изучения кокоа "подвесил" тамошний прогресс-бар. в тигре. в леопарде не повторяется. механизм простой: после пары -setHidden:YES/NO он перестает обновляться, пока в него не кликнешь.
Виталий: а как насчет центрирования контента прямо в NSView, который содержится в NSScrollView?
Виталий: конечно, багов везде полно :)
Насчет таймеров — нахрен они вообще нужны? :)
setHidden? А как насчет removeFromSuperview? :)
т.е. NSView полезет через NSClipView, чтобы узнать -boundSize у NSScrollView и поресайзить себя под этот размер? как-то неизящно для кокоа ;)
я нашел в инете решение через сабкласс NSClipView и ручное создание всей этой троицы, в обход IB. некузяво.
Так это как раз отлично. Так и надо )
А зачем ручное создание, кстати? :)
removeFromSuperView? надо глянуть. не видел. а обратно я его поставить смогу потом? рект запоминать придется?
мне таймеры нужны в программе. для "немгновенной" реакции на действия пользователя (запускать тяжелые вычисления не сразу после клика, а чуть позже). для обновления интерфейса на основании данных от нескольких потоков с предсказуемой периодичностью. есть в них смысл, есть.
зачем ручное - хз. я в IB не нашел куда ткнуть, чтобы после заворачивания вьюхи в скролл, достучаться до клипа.
эту проблему оставил завтра на утро. пока у меня контент отцентрирован по левому нижнему краю (кстати, перевернутая ось OY - тема для четвертого поста, я считаю ;).
Я как раз эти setHidden не использовал (потому что они себя ведут не так, как я ожидал), всегда удалял/добавлял в superview. Обратно можно, только не забыть retain. Насчет ректов не помню.
Таймеры всегда ненавидел. Пусть лучше потоки шлют нотификации чере NSDistributedNotificationCenter. А зачем не сразу запускать?
ну если есть контрол, который надо показывать изредка, то что использовать, как не -setHidden, как считаешь? ;)
зачем не сразу? представь, что есть два метода отрисовки картинки на вьюхе: быстрый и некачественный и медленный и качественный. юзер мышкой подвигал, поменял что-то, вьюха перерисовалась быстро, но некачественно. в этот момент начинается отсчет, скажем, 5-ти секунд. если юзверь за это время решает еще что-то поменять, то 5 секунд начинают отсчитываться заново. а если он ничего уже не делает, то через 5 секунд запускается более "тяжелый" алгоритм отрисовки (тут как раз прогресс появлялся, который не полз ;)
А если view mode в IB поменять на дерево, там оно есть?
Перевернутая ось — ага, совсем непривычно поначалу.
смотреть надо, я сейчас в винде. идея, в принципе, понятна - завтра гляну что там к чему. обидно, что они для скролла не сделали тупой алигн. как для текста. все бы проблемы решились. или анкоры вьюхи читали бы. ан-нет: в левый нижний угол и все.
поначалу... да она по-жизни непривычна.
почему во вьюхе ось вверх, а в картинке - вниз? ;))
а мне рамку Crop'а нужно сделать для картинки. так весело переводить координаты туда-сюда, блин.
думал -isFlipped поможет - хрен там. картинка перевернулась, мать ее ;)
Да, makes sense.
А если setFlipped:YES на картинке и на view? :)
А картинки, кстати, не все с верхнего левого угла "начинаются", на сколько помню :) convertRect:fromView: на них!
И все-таки, про скроллвью. Ты же ведь ему даешь NSView свой с картинкой? Если она маленькая, почему бы в этом NSView и не отцентрировать?
не дал я ладу с этой Cocoa... Увы.
есть потомок NSView, который делает с картинкой то, что мне надо. у него есть метод
- (void) setImage: (NSImage *) img {...}
в нем я retain'ю картинку и максимум, что могу сделать, это вызвать себе же -setFrame: NSMakeFrame(0, 0, w, h) по размерам этой картинки. это я и делаю.
чтобы мелкую картинку центрировать, надо знать размер видимой области скроллвью, это двумя уровнями выше. мне кажется уродливым лезть из класса так "высоко".
я может нечетко выразился. картинка не то, чтобы мелкая. она, вообще говоря, произвольного размера (т.к. это все-таки NS_Zoom_ScrollView) - какой масштаб задам, такого размера она и будет. если она больше клиентской области скроллера, то все шоколадно. а вот если меньше, то надо уже центрировать. ну а про это я уже выше написал
Виталий:
Нормально,
NSView:
- (NSScrollView *)enclosingScrollView
А можно и не лезть, а получать NSNotification о том, что картинку/скроллвью ресайзнули.
Виталий, сейчас писал код с модальным sheet и таймером. Таймер сработал :) У тебя таймер случайно не в run loop другого треда добавляется?