Низкоуровневые детали реализации performSelectorOnMainThread:

Было интересно, знает ли кто-нибудь или имеет указатели на хорошую документацию, в которой обсуждаются детали низкоуровневой реализации метода Какао «performSelectorOnMainThread:».

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

Верно? Неправильный? Спасибо!

Обновление 09:39 AMPST

Спасибо Evan DiBiase и Mecki за ответы, но для пояснения: я понимаю, что происходит в цикле выполнения, но я ищу ответ; «где - метод ставится в очередь? как - информация селектора передается в очередь?» Ищу больше, чем информацию о документах Apple: я их читал

Обновление 14: 21PST

Крис Хансон в своем комментарии поднимает хороший момент: моя цель здесь не в том, чтобы изучить основные механизмы, чтобы использовать их в своем собственном коде. Скорее, меня просто интересует лучшее концептуальное понимание процесса подачи сигналов другому потоку о выполнении кода. Как я уже сказал, мое собственное исследование заставляет меня поверить, что для передачи информации селектора между потоками IPC использует преимущества обмена сообщениями, но я специально ищу информацию конкретный о том, что происходит, поэтому я могу быть уверен, что понимаю вещи правильно. Спасибо!

Обновление 03/06/09

Я объявил награду по этому вопросу, потому что мне бы очень хотелось увидеть ответ на него, но если вы пытаетесь собрать, убедитесь, что вы прочитали все, включая все заданные на данный момент ответы, комментарии как к этим ответам, так и к моему исходному вопросу. , и текст обновления, который я разместил выше. Я ищу детализация самого низкого уровня механизма, используемого performSelectorOnMainThread: и т.п., и, как я упоминал ранее, я подозреваю, что он имеет какое-то отношение к портам Mach, но мне бы очень хотелось знать наверняка. Награда не будет присуждена, если я не смогу подтверждать дать правильный ответ. Спасибо всем!

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

Chris Hanson 30.09.2008 00:57

Хорошая точка зрения! Рассмотрено в редактировании, которое, надеюсь, проливает больше света на то, что мне нужно.

rpj 30.09.2008 01:31

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

Chris Hanson 30.09.2008 05:57

Еще один замечательный момент, но я надеялся, что кто-то может знать или проводить подобное исследование в любом случае. Ну что ж, выстрел стоил! :)

rpj 30.09.2008 21:59

Я тоже хотел бы это знать. Я заметил, что после вызова performSelectorOnMainThread из основного потока NSThread считает мое приложение многопоточным. Странно, а?

Rhythmic Fistman 21.10.2008 12:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
5
2 979
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

документация для метода performSelectorOnMainThread:withObject:waitUntilDone: NSObject говорит:

This method queues the message on the run loop of the main thread using the default run loop modes—that is, the modes associated with the NSRunLoopCommonModes constant. As part of its normal run loop processing, the main thread dequeues the message (assuming it is running in one of the default run loop modes) and invokes the desired method.

Еще одно редактирование:

Чтобы ответить на вопрос комментария:

what IPC mechanism is being used to pass info between threads? Shared memory? Sockets? Mach messaging?

NSThread хранит внутреннюю ссылку на основной поток, и через эту ссылку вы можете получить ссылку на NSRunloop этого потока. NSRunloop внутри представляет собой связанный список, и путем добавления объекта NSTimer в цикл выполнения создается и добавляется в список новый элемент связанного списка. Таким образом, можно сказать, что это разделяемая память, связанный список, который фактически принадлежит основному потоку, просто модифицируется из другого потока. Существуют мьютексы / блокировки (возможно, даже объекты NSLock), которые гарантируют, что редактирование связанного списка является потокобезопасным.

Псевдокод:

// Main Thread

for (;;) {
    lock(runloop->runloopLock);
    task = NULL;
    do {
        task = getNextTask(runloop);
        if (!task) {
            // function below unlocks the lock and
            // atomically sends thread to sleep.
            // If thread is woken up again, it will
            // get the lock again before continuing
            // running. See "man pthread_cond_wait"
            // as an example function that works
            // this way
            wait_for_notification(runloop->newTasks, runloop->runloopLock);
        }
    } while (!task);
    unlock(runloop->runloopLock);
    processTask(task);
}


// Other thread, perform selector on main thread
// selector is char *, containing the selector
// object is void *, reference to object

timer = createTimerInPast(selector, object);
runloop = getRunloopOfMainThread();
lock(runloop->runloopLock);
addTask(runloop, timer);
wake_all_sleeping(runloop->newTasks);
unlock(runloop->runloopLock);

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


Я все еще не понимаю, в чем вопрос. селектор - это не что иное, как строка C, содержащая имя вызываемого метода. Каждый метод является обычной функцией C, и существует таблица строк, содержащая имена методов в виде строк и указателей на функции. Это основные принципы работы Objective-C.

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

Не совсем реализация, но довольно близко к ней:

Каждый поток в Какао имеет NSRunLoop (он всегда там, вам никогда не нужно создавать его для потока). PerformSelectorOnMainThread создает объект NSTimer, такой как это, который срабатывает только один раз, и время срабатывания которого уже находится в прошлом (поэтому запускается немедленно), затем получает NSRunLoop основного потока и добавляет туда объект таймера. Как только основной поток переходит на праздный, он ищет в своем цикле выполнения следующее событие для обработки (или переходит в спящий режим, если нечего обрабатывать и снова просыпается, как только событие добавлено) и выполняет его. Либо основной поток занят, когда вы планируете вызов, и в этом случае он будет обрабатывать событие таймера, как только он завершит свою текущую задачу, либо он в данный момент спит, и в этом случае он будет разбужен добавлением события и обрабатывает его немедленно.

Хороший источник, чтобы узнать, как Apple представляет собой скорее всего делаю это (никто не может сказать наверняка, поскольку все-таки его исходный код закрыт), это GNUStep. Поскольку GCC может обрабатывать Objective-C (это не просто расширение, которое поставляет только Apple, с ним может справиться даже стандартный GCC), однако, наличие Obj-C без всех базовых классов, поставляемых Apple, довольно бесполезно, сообщество GNU попыталось повторно -реализуйте наиболее распространенные классы Obj-C, которые вы используете на Mac, и их реализация - OpenSource.

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

Распакуйте это и посмотрите на реализацию NSThread, NSObject и NSTimer для подробностей. Думаю, Apple не сильно отличается от этого, я, вероятно, мог бы доказать это с помощью gdb, но почему они сделали это иначе, чем этот подход? Это умный подход, который очень хорошо работает :)

Большое спасибо за согласованные усилия Mecki, и я комментирую, чтобы вы знали, почему я не помечаю ваш пост как правильный ответ: вопрос не в таймерах и селекторах; какой механизм IPC используется для передачи информации между потоками? Общая память? Розетки? Обмен сообщениями Mach?

rpj 30.09.2008 22:44

И хотя GNUStep в целом является отличным ресурсом, он здесь не помогает, потому что он, по сути, написан поверх того, что выглядит как API POSIX, который в OS X реализован поверх подсистем более низкого уровня, таких как Mach. Как говорит Крис Хэнсон, ответ неуловим, потому что это, скорее всего, с закрытым исходным кодом.

rpj 30.09.2008 22:47

На самом деле Mac OS - одна из немногих, действительно совместимых с POSIX систем, и предположение, что Apple не использует вызов POSIX, если он есть, а вместо этого использует какой-то неясный вызов Mach, - это чистая спекуляция. Фактически использование DTrace на Leopard показывает, что Obj-C использует множество вызовов POSIX.

Mecki 01.10.2008 14:11

Как сказал Меки, более общий механизм, который можно использовать для реализации -performSelectorOn…, - это NSTimer.

NSTimer является бесплатным мостом для подключения к CFRunLoopTimer. Реализацию CFRunLoopTimer - хотя не обязательно тот, который фактически используется для обычных процессов в OS X - можно найти в CFLite (подмножество CoreFoundation с открытым исходным кодом; пакет CF-476.14 в Исходный код Darwin 9.4. (CF-476.15, соответствующий OS X 10.5). .5, пока недоступен.)

Ответ принят как подходящий

Да, он использует порты Mach. Происходит вот что:

  1. Блок данных, инкапсулирующий информацию о выполнении (целевой объект, селектор, необязательный аргумент объекта для селектора и т. д.) Помещается в очередь в информации цикла выполнения потока. Это делается с помощью @synchronized, который в конечном итоге использует pthread_mutex_lock.
  2. CFRunLoopSourceSignal вызывается, чтобы сигнализировать, что источник готов к срабатыванию.
  3. CFRunLoopWakeUp вызывается, чтобы цикл выполнения основного потока знал, что пора просыпаться. Это делается с помощью mach_msg.

Из документов Apple:

Version 1 sources are managed by the run loop and kernel. These sources use Mach ports to signal when the sources are ready to fire. A source is automatically signaled by the kernel when a message arrives on the source’s Mach port. The contents of the message are given to the source to process when the source is fired. The run loop sources for CFMachPort and CFMessagePort are currently implemented as version 1 sources.

Я сейчас смотрю на трассировку стека, и вот что она показывает:

0 mach_msg
1 CFRunLoopWakeUp
2 -[NSThread _nq:]
3 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:]
4 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:]

Установите точку останова на mach_msg, и вы сможете ее подтвердить.

Другие вопросы по теме