Simples Projekt mit NSOperation, für Einsteiger geeignet

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Simples Projekt mit NSOperation, für Einsteiger geeignet

    Hello, It's me,
    da ich bei meinem Project das Problem hatte, die langwierigen Berechnungen auf einen neuen thread zu schubsen,
    hat mir die Entwicklung dieses kleinen Projekts auf den Sprung geholfen.
    Wenn ihr Verbesserungen bezüglich der Sicherheit anführen könnt, nehme ich das gerne an.

    Uwe
    Dateien
    • VROperation.zip

      (43,12 kB, 196 mal heruntergeladen, zuletzt: )
    How come I can't see me in my mirror ?
  • warum sollte man eine operationqueue verwenden für genau eine nebenläufige aktion?
    bei mehreren macht das mit dem MUTABLE info-objekt auch keinen sinn (und führt sogar zu schweren fehlern).
    wenn es um performance geht, ist die geschichte mit den notifications vielleicht auch nicht optimal (blocks sind auch flexibler).
    auch die keys finde ich komisch (wozu sind die überhaupt vorhanden)?
    du könntest setter/getter verwenden
    du könntest doppelten code eliminieren (man muss eine ivar nicht 2 mal auf NO setzen (vor allem weil sie eh schon auf NO steht also 2 mal zuviel)).
    nur schnell überflogen das ganze - soll also keine vollständige analyse sein!
  • Lieber gritsch,
    danke für deine Antwort.
    in meinem Projekt brauche ich mindestens zwei Operationen, es könnten auch drei sein aber da sie voneinander abhängig sind, bringt das wohl sexuell nix. Zwei brauche ich, weil ich Zwischenergebnisse darstellen möchte.
    Der Projektteil berechnet eine Optimierung und benutzt Zufallszahlen, es kann vorkommen, dass ich auf eine Bergspitze kraxle und dann feststelle, dass neben mir ein Berg zu sehen ist, der höher ist. Übertragen heisst das, wenn die Routine bei einem Wert, der nicht das erwartete Optimum ist, kann ich abbrechen und es noch mal versuchen.

    Unter welchen Bedingungen ist denn mit einer ungewollten Änderung einer Variablen zu rechnen ? oder mit einem Absturz ?
    In dem veränderlichen WörterBuch (Viertopfzerknalltreibling ) befinden sich mutableArrays, die u.U. mit replaceObject: atIndex: aktualisiert werden.
    Das ist bequem. u.U. bedeutet, dass ich die Berechnung eines Wertes überspringen kann, wenn sein Optimum erreicht ist.
    Am Anfang sind das 400 Werte, bei 0 habe ich die Spitze des höchsten Berges erreicht.

    Wie man mit Blocks operiert, weiss ich nicht, habe auch kein Beispiel gefunden.

    Um die Performance mache ich mir keine Sorgen, anfänglich gibt es noch viele Notifikationen, wenn ich mich am Fuss des Berges befinde.

    Die keys habe ich mir so angewöhnt, wegen der Konsistenz.

    Die Struktur dieses Progämmchen habe von Apple's "NSOperationSample" abgekupfert, dann muss es doch stimmen, oder
    Ich werde mir das mit der Redundanz ansehen.

    Uwe
    How come I can't see me in my mirror ?
  • Stimmt, das doppelte Lordchen war doppelt vorhanden, Flüchtigkeitsfehler.
    In der Methode completeOperation wird das Ende der Operation eingeläutet. Für mich ist das o.k.

    Statt der Notification könnte ich auch

    Quellcode

    1. - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait


    nehmen, was sagst du dazu ?
    How come I can't see me in my mirror ?
  • Naja, vieles von der angemerkten Kritik stimmt, ist aber nicht Mutlithreading-relevant: Mehrfache Initialisierung ist allgemein sinnlos, tut aber in diesem Fall nur mäßig weh (insbesondere wenn die NSOperation irgendwann wirklich etwas Sinnvolles tut). Mutable-Objekte sind allgemein kein schlauer Ansatz zur Kommunikation zwischen Threads (immutable ist per Design thread-safe), in diesem Fall macht es aber keinen Unterschied. NSExceptions ohne weiteres wegzufangen ist keine gute Idee, weil zu diesem Zeitpunkt die Runtime vermutlich schon im Eimer ist. Dictionaries zum Initialisieren der Operation kann man als Überverallgemeinerung und so als Code Smell sehen usw. Kurz: Die Kritik stimmt, wäre aber speziell für die NSOperation-Frage nicht kritisch.

    Aber es gibt ein grundlegenderes Problem: Weder NSNotifications noch KVO lösen das Problem der Synchronisation am Ende der Operation. Sie laufen synchron im auslösenden Thread weiter. Konkret: Wenn du am Ende der Operation eine Notification rauswirfst, kommt diese im Document auf dem Thread der Operation an, nicht im Hauptthread. Dort setzt du (synchron) Werte in NSTextFields, also Unterklassen von NSViews. Auf alle Views sollte ausschließlich im Hauptthread zugegriffen werden. Sowas kann funktionieren, es kann aber auch ganz böse in die Hose gehen (und sowas ist die Debugging-Hölle). Die Synchronisation zum Ende der Operation muss über entsprechende Mechanismen laufen, z.B. über performSelectorOnMainThread:, NSRunLoops, NSLocks oder ähnliches.
    Multigrad - 360°-Produktfotografie für den Mac
  • Hallo,

    damit kann man jetzt garnix anfangen.
    denn die interessanten sachen fehlen (zb was du mit dem lock anstellen willst, was du in der operationResult-methode machst etc...

    Das habe ich mir Gestern schon gedacht, aber ich wollte es nicht so gemein schreiben und wollte nicht klugscheißen.

    Aber es fehlt wirklich der Sinn von den Operations in dem "Beispiel"-Projekt.
    Auf die Sachen, auf die es ankommt, wird gar nicht eingegangen.

    Ich habe das jetzt nicht mehr auf dem Radar, aber ich meine, dass die -main schon falsch ist, da diese einen eigenen AutoreleasePool benötigt.
    So war das zumindest früher, wenn ich mich richtig entsinne und bei ARC weiß ich es nicht. Könnte mir aber gut vorstellen, dass der Teil in @autorelease gekapselt werden muss.
    Die Doku weiß sicherlich mehr dazu.

    Also sinnvoller wäre es eine Tabelle zu erstellen, zehn Zeilen hinzuzufügen, die den Inhalt von zehn Operationen aufzeigt.
    Das hätte ich jetzt bei einem Beispiel-Projekt "erwartet".

    Viele Grüße

  • damit kann man jetzt garnix anfangen.denn die interessanten sachen fehlen (zb was du mit dem lock anstellen willst, was du in der operationResult-methode machst etc...

    Ich entnahm der vorherigen Kritik, dass mutable Objekte nicht thread save sind, erfüllt der Lock diese Bedingung nicht ?

    Dass du Interesse an meinem Projekt erkennen lässt, freut mich - das aber umfassend zu erklären, würde den Rahmen sprengen.
    Ich beschränke mich auf den Teil, der mit NSOperation zusammen hängt. Ich hab ein Bild angehängt auf dem zwei Ebenen zu sehen sind,
    in denen Schwingungen stattfinden. Nun sind diese Schwingungen gegenläufig (CW und CCW), so dass der Abstand nicht konstant ist.
    Das will ich aber erreichen. Ich verschiebe die Ebenen auf der Diagonalen so, dass diese Konstanz erreicht wird. Ich erwarte, dass die Änderungen
    des Offsets eine harmonische Kurve ergeben. Man könnte, ich kann das nicht, diese Aufgabe mit der Vektorrechnung lösen, ich benutze eine
    Optimierungsmethode, einen sog. genetischen Algorithmus, der mit dem Zufall operiert (und er würfelt doch).
    Der innere Berechnungsblock wird, grob geschätzt, 20 Millionen mal aufgerufen, alle 50000 Berechnungen will ich ein Zwischenergebnis haben,
    dass ich dann auf dem Screen darstelle.

    Ich hoffe, du willst jetzt noch mehr wissen.
    Uwe
    Dateien
    • Waves.png

      (31,68 kB, 240 mal heruntergeladen, zuletzt: )
    How come I can't see me in my mirror ?
  • Dein Lock sichert den atomaren Zugriff auf einzelne Properties mit mehreren Threads. Ob das Gesamtding thread-safe ist, hängt davon ab, wie du das Dictionary benutzen willst. Wenn z.B. zwischen den Änderungen verschiedener Properties ein inkonsistenter Zustand im Dictionary steht, hast du immer noch ein Problem. Auch das könnte man mit händischen Locks lösen, aber das kann irgendwann sehr ekelig werden.

    ich habe das Gefühl, dass du gegen die Konzepte ankämpfst: Dein Info-Dictionary versucht, eine permanente Verbindung zwischen den Threads zu erstellen. Mach' das nicht - Threads sollen möglichst unabhängig voneinander sein, nicht eng gekoppelt. Konfiguriere die NSOperation mit einer Rechenaufgabe, die so bleibt. An passenden Stellen informiert sie den Hauptthread über ihre Ergebnisse (mit den erwähnten Methoden, über ein immutables Ergebnisobjekt). Wenn sich die Rechenaufgabe ändert, brich die alte NSOperation ab und starte eine neue. So muss niemals ein Thread auf den anderen warten und du hast keine mutablen Objekte, bei denen man nie so genau weiß, welchem Thread sie gehören und bei denen man händisch alle nebenläufigen Fälle pflegen muss.
    Multigrad - 360°-Produktfotografie für den Mac
  • Mensch mattik , du erinnerst mich an GitHub's team: 'Talk to a Human'.
    Also, ich möchte es so machen:
    die Instanziierung mit einem immutable Dictionary, dann eiin mutable Dictionary für die Ergebnisse kreieren , das ich dann als letzte Aktion vor performSelector... immutable mache.
    Ich frage dich, ob ich den ganzen Strauss von Variablen, die ich für die Berechnung brauche, thread save behandeln muss, ?
    Doch wohl nicht, wenn ich sie als @property(strong) oder @property(readwrite, nonatomic) einrichte ?
    How come I can't see me in my mirror ?
  • Dylans Ghost schrieb:

    die Instanziierung mit einem immutable Dictionary, dann eiin mutable Dictionary für die Ergebnisse kreieren , das ich dann als letzte Aktion vor performSelector... immutable mache.
    Ich frage dich, ob ich den ganzen Strauss von Variablen, die ich für die Berechnung brauche, thread save behandeln muss, ?
    Doch wohl nicht, wenn ich sie als @property(strong) oder @property(readwrite, nonatomic) einrichte ?

    Bei atomic properties ist der Zugriff atomar, sprich synchronisiert, aber eigentlich spielen die Properties kaum eine Rolle. Deine Threads kommunizieren ja nicht mit Properties, sondern mit Objekten. Wenn du ein Einwegobjekt für die Ergebnisse erzeugst, das ausschließlich Informationen an den Hauptthread übermittelt und anschließend von der Operation vergessen wird, musst du es nicht immutable machen. Es kommt da nicht auf den Typen der Objekte direkt an, mit anderen Worten: In Immutable-Objekten steckt kein Mechanismus, der sie thread-safe macht - sie lassen sich halt nur nicht ändern.

    Du kannst immer dann in Schwierigkeiten kommen, wenn du auf veränderliche Variablen (oder andere Ressourcen) von mehreren Threads aus unsynchronisiert zugreifst. Das sind die Racing Conditions, die sehr viel Ärger machen können. Un das zu verhindern, muss man also entweder a) Veränderlichkeit (genauer: Veränderungen) vermeiden oder b) gemeinsames Zugreifen vermeiden oder c) von Hand synchronisieren. Das gilt in beide Richtungen (Hauptthread -> Operation und andersrum) und ist für alle Ressourcen gültig, auch für Unterobjekte in Containern. c) ist mühsam und fehlerträchtig, daher will man das wenn möglich vermeiden. Bleibt a) oder b). a) geht über immutable Objekte. Bei generischen Containern wie Dictionaries, Arrays oder Sets hat man immer das Problem, dass man nicht weiß, ob da in den Tiefen noch etwas mutables, gemeinsam genutztes bleibt. Deshalb ist es sinnvoll, dafür keine generischen Container zu verwenden, sondern eigene Objekte, die die Daten spezifisch abbilden (ist eh sauberer, besser und einfacher - Dictionaries für alles zu verwenden ist eine Unsitte...). Eine Möglichkeit für b) ist es, über Einwegnachrichten zu kommunizieren, die keine Ressourcen enthalten, die vom Sender weiterverwendet werden. Das ist quasi dein Fall der Ergebnisnachrichten - und da diese schon b) genügen, ist a) für sie nicht mehr notwendig.
    Multigrad - 360°-Produktfotografie für den Mac