ManagedObjectContext + Threads

  • ManagedObjectContext + Threads

    Hallo,

    leidiges Thema: CoreData + Threads. Ich habe dazu die Appledokumentation recht oft studiert und mir Gedanken gemacht. Es existrieren unterschiedliche Ansätze, wie CoreData gemeinsam mit Threads genutzt werden kann - u.a:

    Ansatz 1: einen managedObjectContext pro Thread + object IDs weiterleiten.
    Ansatz 2: einen managedObjectContext sowie einen persistentStoreCoordinator pro Thread und ebenfalls nur object IDs weiterleiten.

    Mir ist nicht ganz klar, wie sich diese beiden Ansätze in der Praxis unterscheiden. Die Apple Dokumentation äußert sich zu Ansatz 1 wie folgt:

    "If you want to aggregate a number of operations in one context together as if a virtual single transaction, you can lock the persistent store coordinator to prevent other managed object contexts using the persistent store coordinator over the scope of several operations."

    Zu Ansatz 2:

    "Using a separate persistent store coordinator for each thread allows for completely concurrent operations."

    Okay. Der erste Unterschied wäre also, dass ich bei Ansatz 2 komplett aufs Locking verzichten kann. Das hört sich gut an. Ansatz 2 ist also die "fehlerunanfälligere" - falls man das so pauschal ausdrücken möchte. Richtig?

    Kleines Beispiel, wie man Ansatz 2 realisiert:

    Man nehme einen normalen Controller (NSObject Subclass) - mit einer Action: doSomethingComplicatedWithSomeManagedObjects. Sobald die Action getriggert wird - durch einen Button o.ä müsste folgendes erledigt werden:

    1. einen persistentStoreCoordinator erzeugen, welcher das selbe managedObjectModel wie der "HauptmanagedObjectContext" (= der, des mainThreads) hat.

    2. diesem persistentStoreCoordinator einen persistentStore zuweisen.

    3. ein managedObjectContext erzeugen und diesem den neuen persistentStoreCoordinator zuweisen.

    4. einen Thread erzeugen und diesem den erzeugten managedObjectContext sowie eine oder mehrere objectIDs (aus dem HauptmanagedObjectContext), welche für ihn von Interesse sind übergeben.

    5. den Thread starten

    6. der Thread macht folgendes: er holt sich mit Hilfe "seines" managedObjectContexts und dessen objectWithID: Methode sowie der an ihn übergebenen objectIDs (aus dem HauptmanagedObjectContext - siehe 4.) die managedObjects, welche allerdings nun den managedObjectContext des Threads referenzieren.

    7. der Thread erledigt ganz normal seine Arbeit und am Ende wird sein managedObjectContext mit dem HauptmanagedObjectContext vereint.

    Ist das alles so "okay"? Zu umständlich? Unsicher?
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • Nur mal interessehalber: Wozu willst du das verwenden? Erhoffst du dir Performancegewinne auf Dualcore-Maschinen oder geht es eher darum, dass das Interface während einer langwierigen Berechnung o.ä. benutzbar bleibt?

    Ich habe auch mal vor einer Weile die Doku zu dem Thema gewälzt, bin in meinem Fall aber letztlich zum Schluss gekommen, dass Threads überflüssig waren, da es mir nur darum ging, dass ein Benutzer eine langwierige Operation problemlos abbrechen können sollte und mir dafür beginModalSessionForWindow: etc. absolut ausreichend war -- und auf dessen Dokumentation ich nur durch Zufall überhaupt gestoßen bin, da ich mich an ein Zitat aus dem Hillegass erinnerte:

    Many people who think they need to create a multithreaded application later realize that their application would have been more efficient and easier to write if they had used the run loop more wisely. It is rarely necessary to create multithreaded applications.


    Das kann in deinem Fall natürlich auch komplett anders sein, daher frag ich. ;)
  • Meine Anwendung exportiert Daten in verschiedene Formate. Der Vorgang des Exportierens nimmt teilweise einige Minuten in Anspruch. Da ich allerdings einen ProgressIndicator anzeigen möchte darf der Exportvorgang den mainThread nicht wirklich blockieren.

    Ist damit die Erzeugung eines Threads gerechtfertigt?
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • Meine Anwendung exportiert Daten in verschiedene Formate. Der Vorgang des Exportierens nimmt teilweise einige Minuten in Anspruch. Da ich allerdings einen ProgressIndicator anzeigen möchte darf der Exportvorgang den mainThread nicht wirklich blockieren.

    Ist damit die Erzeugung eines Threads gerechtfertigt?

    Einen ProgressIndicator anzeigen kannst du problemlos auch ohne zusätzlichen Thread. Schau dir ggf. mal die Doku zu runModalSession: (NSApplication) an, da wird die Vorgehensweise ganz gut mit einem Beispiel erläutert.

    EDIT: Kommt natürlich auf die Art deiner Exportfunktion an, ob sie sich gut durch eine Schleife abbilden lässt.
  • Also,auch wenn das erneut etwas neben dem Thema liegt: Für einen Progress-Indicator benötigst du keinen Thread, schon gar keinen, der auf deinem Model arbeitet.

    Ansonsten habe ich hier ja schon einige Male geäußert, dass Threads in aller Regel eine Applikation langsamer, nicht schneller machen. (Das stimmt natürlich mittlerweile nicht mehr so, weil es Multicores gibt.) Aber wenn deine Applikation ohnehin "inhaltlich" nicht mehr nutzbar ist, sind Threads eigentlich eher fern liegend. Und für ein bisschen Animation benötigt man wirklich keinen Lock auf das Modell.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Aber doch nur hinsichtlich des PI?

    BTW: Du kannst in solchen Fällen heraus selbst das View neu zeichnen. Hierzu dient -display. Außerdem kannst du dem PI sagen, dass er sich selbst threaden soll. -usesThreadedAnimation: oder so ähnlich.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Ah, ich vergaß!

    Hier kann wirklich ein Thread erforderlich sein. Aber da hast du nichts mit CD am Hut, weil du einfach bei Klick hierauf eine Signalvariable setzen kannst, die du im anderen Thread liest. Dabei erfolgen ja keine Zugriffe auf dein Model.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Doch. Die Exportlogik ist im NSManagedObject Subclass implementiert. Es wird mindestens lesend auf das Model zugegriffen - eventuell auch schreibend. Du meinst ich sollte einen weiteren Thread schaffen, der immer wieder überprüft, ob der Exportvorgang noch läuft/nicht abgebrochen wurde? Hm - momentan fange ich einfach eine Notification ab, welche mich über das Ende des Threads informiert. Naja - ich schau mir alles nochmal genau an.
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • Ja, ich meine, dass du in deinem einem Thread das eigentliche Speichern durchziehst und in dem anderen *nur* auf den Button wartest. Dann müsste ein Locking eigentlich obsolet sein, weil es ja keinen Konflikt gibt.


    Du musst im Speicher-Thread freilich alle paar Durchläufe oder wie auch immer das Flag abfragen.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Du musst im Speicher-Thread freilich alle paar Durchläufe oder wie auch immer das Flag abfragen.

    Dann kannst du dir den Thread aber auch eigentlich gleich sparen. Performancegewinne hast du keine, im Ergebnis sieht es mit einer NSModalSession gleich aus und "less code is better code"... ;)
  • Original von Tom9811
    Er will das Speichern ja in einer Schleife unternehmen. So verstehe ich ihn jedenfalls. Dann wird er aus der modal Session keine Events erhalten. Ich sehe jedenfalls nicht, wie.

    In dem er in der Schleife regelmäßig

    Quellcode

    1. [NSApp runModalSession:session];
    aufruft. Funktioniert bestens. Siehe auch hier.

    Michael
  • Der Witz an der modal Session ist ja gerade, dass man die Events in dem definierten Fenster (z.B. Sheet mit ProgressIndicator, Abbrechen-Button etc.) erhält.
    Also die Exportfunktion sähe bspw. so aus:

    Quellcode

    1. //Sheet einblenden...
    2. exportSession = [NSApp beginModalSessionForWindow:theSheet];
    3. for (;;) {
    4. if ([NSApp runModalSession:session] != NSRunContinuesResponse)
    5. break;
    6. [self doSomeWork];
    7. }
    8. [NSApp endModalSession:session];
    9. //Sheet wieder ausblenden...

    und in der Action des Abbrechen-Buttons stünde:

    Quellcode

    1. [NSApp stopModal];

    exportSession noch als Instanzvariable vom Typ NSModalSession definiert und fertig.