Dettagli di basso livello sull'implementazione di performSelectorOnMainThread:

StackOverflow https://stackoverflow.com/questions/149388

  •  02-07-2019
  •  | 
  •  

Domanda

Mi chiedevo se qualcuno sapesse, o avesse suggerimenti per una buona documentazione che discute, i dettagli di implementazione di basso livello del metodo "performSelectorOnMainThread:" di Cocoa.

La mia ipotesi migliore, e penso che sia probabilmente abbastanza vicina, è che utilizza porte mach o un'astrazione sopra di esse per fornire comunicazioni intra-thread, passando informazioni selettrici come parte del messaggio mach.

A destra? Sbagliato? Grazie!

Aggiornamento 09:39 AMPST

Grazie Evan DiBiase e Mecki per le risposte, ma per chiarire: capisco cosa succede nel ciclo di corsa, ma quello a cui sto cercando una risposta è; " dove il metodo viene messo in coda? come le informazioni del selettore vengono passate nella coda? " Cerchi qualcosa di più delle informazioni sui documenti di Apple: le ho lette

Aggiornamento 14: 21PST

Chris Hanson evidenzia un buon punto in un commento: il mio obiettivo qui non è quello di apprendere i meccanismi sottostanti per trarne vantaggio nel mio codice. Piuttosto, sono solo interessato a una migliore comprensione concettuale del processo di segnalazione di un altro thread per eseguire il codice. Come ho detto, la mia stessa ricerca mi porta a credere che sfrutti i messaggi di mach per IPC per passare le informazioni di selezione tra i thread, ma sto specificatamente cercando informazioni concrete su ciò che sta accadendo, quindi posso essere sicuro di capire le cose correttamente . Grazie!

Aggiornamento 03/06/09

Ho aperto una generosità a questa domanda perché mi piacerebbe davvero vederne la risposta, ma se stai cercando di raccogliere assicurati di leggere tutto , comprese tutte le risposte attualmente poste, commenti sia a queste risposte che alla mia domanda originale e al testo di aggiornamento che ho pubblicato sopra. Sto cercando il dettaglio di livello più basso del meccanismo utilizzato da performSelectorOnMainThread: e simili, e come ho già detto in precedenza, sospetto che abbia qualcosa a che fare con Mach porti ma mi piacerebbe saperlo con certezza. La taglia non verrà assegnata a meno che non possa confermare la risposta fornita sia corretta. Grazie a tutti!

È stato utile?

Soluzione

Sì, utilizza le porte Mach. Quello che succede è questo:

  1. Un blocco di dati che incapsula le informazioni di esecuzione (l'oggetto di destinazione, il selettore, l'argomento oggetto opzionale al selettore, ecc.) è accodato nelle informazioni del ciclo di esecuzione del thread. Questo viene fatto usando @synchronized , che alla fine usa pthread_mutex_lock .
  2. Viene chiamato CFRunLoopSourceSignal per segnalare che la sorgente è pronta per essere attivata.
  3. Viene chiamato CFRunLoopWakeUp per far sapere al ciclo di esecuzione del thread principale che è ora di svegliarsi. Questo viene fatto usando mach_msg.

Dai documenti Apple:

  

I sorgenti della versione 1 sono gestiti dal ciclo di esecuzione e dal kernel. Queste fonti usano le porte Mach per segnalare quando le fonti sono pronte a sparare. Una fonte viene automaticamente segnalata dal kernel quando arriva un messaggio sulla porta Mach della fonte. Il contenuto del messaggio viene fornito all'origine da elaborare quando viene generata l'origine. Le origini del ciclo di esecuzione per CFMachPort e CFMessagePort sono attualmente implementate come origini versione 1.

Sto guardando una traccia dello stack in questo momento, e questo è ciò che mostra:

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

Imposta un breakpoint su mach_msg e sarai in grado di confermarlo.

Altri suggerimenti

Un'altra modifica:

Per rispondere alla domanda del commento:

  

a quale meccanismo IPC viene utilizzato   passare informazioni tra thread? Condivisa   memoria? Prese? Messaggi di Mach?

NSThread memorizza internamente un riferimento al thread principale e tramite quel riferimento è possibile ottenere un riferimento a NSRunloop di quel thread. Un NSRunloop internamente è un elenco collegato e aggiungendo un oggetto NSTimer al runloop, viene creato e aggiunto all'elenco un nuovo elemento dell'elenco collegato. Quindi si potrebbe dire che è memoria condivisa, l'elenco collegato, che in realtà appartiene al thread principale, viene semplicemente modificato all'interno di un thread diverso. Esistono mutex / blocchi (possibilmente anche oggetti NSLock) che assicurano che la modifica dell'elenco collegato sia thread-safe.

Codice pseudo:

// 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);

Naturalmente questo è troppo semplificato, la maggior parte dei dettagli sono nascosti tra le funzioni qui. Per esempio. getNextTask restituirà un timer solo se il timer dovrebbe essere già stato attivato. Se la data di attivazione di ogni timer è ancora in futuro e non vi sono altri eventi da elaborare (come una tastiera, un evento del mouse dall'interfaccia utente o una notifica inviata), restituirà NULL.


Non sono ancora sicuro di quale sia la domanda. Un selettore non è altro che una stringa C contenente il nome di un metodo chiamato. Ogni metodo è una normale funzione C ed esiste una tabella di stringhe, contenente i nomi dei metodi come stringhe e puntatori a funzioni. Queste sono le basi del funzionamento effettivo di Objective-C.

Come ho scritto sotto, viene creato un oggetto NSTimer che ottiene un puntatore all'oggetto di destinazione e un puntatore a una stringa C contenente il nome del metodo e quando il timer scatta, trova il metodo C giusto da chiamare usando la stringa tabella (quindi necessita del nome stringa del metodo) dell'oggetto target (quindi necessita di un riferimento ad esso).

Non esattamente l'implementazione, ma piuttosto vicina ad essa:

Ogni thread in Cocoa ha un NSRunLoop (è sempre lì, non è mai necessario crearlo per un thread). PerformSelectorOnMainThread crea un oggetto NSTimer come questo , uno che spara solo una volta e dove il tempo di fuoco si trova già in passato (quindi ha bisogno di sparare immediatamente), quindi ottiene NSRunLoop del thread principale e aggiunge l'oggetto timer lì. Non appena il thread principale diventa inattivo , cerca il prossimo evento nel suo Runloop da elaborare (o va in sospensione se non c'è nulla da elaborare e viene riattivato non appena viene aggiunto un evento ) e lo esegue. O il thread principale è occupato quando si pianifica la chiamata, nel qual caso elaborerà l'evento timer non appena avrà terminato la sua attività corrente o è inattivo al momento, nel qual caso verrà svegliato aggiungendo l'evento e lo elabora immediatamente.

Una buona fonte per cercare in che modo Apple sta molto probabilmente (nessuno può dirlo con certezza, come del resto la sua fonte chiusa) è GNUStep. Poiché il GCC può gestire Objective-C (non è solo un'estensione fornita solo da Apple, anche il GCC standard può gestirlo), tuttavia, avere Obj-C senza tutte le classi di base fornite da Apple è piuttosto inutile, la comunità GNU ha provato a ri -implementa le classi Obj-C più comuni che usi su Mac e la loro implementazione è OpenSource.

Qui puoi scaricare una fonte recente pacchetto.

Disimballalo e dai un'occhiata all'implementazione di NSThread, NSObject e NSTimer per i dettagli. Immagino che Apple non lo stia facendo in modo molto diverso, probabilmente potrei provarlo usando gdb, ma perché dovrebbero farlo in modo molto diverso da questo approccio? È un approccio intelligente che funziona molto bene :)

Il documentazione per performSelectorOnMainThread di NSObject: withObject: waitUntilDone: metodo :

  

Questo metodo mette in coda il messaggio sul ciclo di esecuzione del thread principale utilizzando le modalità del ciclo di esecuzione predefinite & # 8212; ovvero, le modalità associate a NSRunLoopCommonModes costante. Come parte della sua normale elaborazione del ciclo di esecuzione, il thread principale elimina il messaggio (supponendo che sia in esecuzione in una delle modalità predefinite del ciclo di esecuzione) e invoca il metodo desiderato.

Come ha detto Mecki, un meccanismo più generale che potrebbe essere utilizzato per implementare -performSelectorOn ... è NSTimer .

NSTimer è gratuito con ponte su CFRunLoopTimer . Un'implementazione di CFRunLoopTimer - sebbene non necessariamente quella effettivamente utilizzata per i normali processi in OS X - & nbsp; può essere trovata in CFLite (sottoinsieme open-source di CoreFoundation; pacchetto CF-476.14 in codice sorgente Darwin 9.4 . (CF-476.15, corrispondente a OS X & nbsp; 10.5.5 , non è ancora disponibile.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top