performSelectorOnMainThread -> App friert ein

  • performSelectorOnMainThread -> App friert ein

    Meine App friert manchmal ein und ich bin mir nicht ganz sicher warum.
    Ich denke es hat was mit performSelectorOnMainThread:withObject:waitUntilDone: zu tun

    Und zwar habe ich einen Backgroundthread. Diesen erzeuge ich so:

    Quellcode

    1. - (void)start{
    2. NSThread* updateThread = [[NSThread alloc]initWithTarget:self selector:@selector(threadedTimer) object:nil];
    3. [updateThread setName:@"UpdateThread"];
    4. [updateThread start];
    5. }
    6. - (void)threadedTimer{
    7. @autoreleasepool {
    8. DDLogInfo(@"Create RunLoop");
    9. NSRunLoop *TimerRunLoop = [NSRunLoop currentRunLoop];
    10. UpdateRoutine *updateRoutine = [[UpdateRoutine alloc]init];
    11. [updateRoutine serviceCheck];
    12. [NSTimer scheduledTimerWithTimeInterval:15.0 target:updateRoutine selector:@selector(serviceCheck) userInfo:nil repeats:YES];
    13. [TimerRunLoop run];
    14. }
    15. }
    Alles anzeigen

    Wenn der backgroundthread seine arbeite erledigt hat, sendet er folgendes:

    Quellcode

    1. [[NSNotificationCenter defaultCenter] postNotificationName:@"Done" object:object];

    Dies kommt dann hier an:

    Quellcode

    1. -(void)done:(NSNotification*)notification{
    2. DDLogWarn(@"Done Received");
    3. [self performSelectorOnMainThread:@selector(doSomething:) withObject:[notification object] waitUntilDone:NO];
    4. }


    In den meisten Fällen wird doSomething aufgerufen aber manchmal hängt sich das Programm auch einfach auf.
    Ich bekomme keine Fehlermeldung, sowohl der Mainthread als auch der Backgroundthread frieren ein.

    Woran könnte das liegen?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Erebos1988 ()

  • Versteh nicht warum der Code jetzt nicht mehr formatiert ist. War er vor dem kopieren noch.


    Als ich mit NSThreads anfing, wusste ich noch nichts von NSOperations.
    Können die denn auch einen Timer aufnehmen?
    Dieser Backgroundthread läuft nämlich dauerhaft.
    Bieten NSOperations sonstige Vorteile?
    Wenn ja, werde ich mich da mal einlesen.

    Ich habe gerade gelesen, das es durch die NSNotifications auch zu deadlocks kommen kann.
    Könnte also auch sein, das die notification der Fehler ist.
  • Erebos1988 schrieb:

    Könnte also auch sein, das die notification der Fehler ist.

    Du übergibst der doSomething: Methode ja auch das Objekt aus der Notification. Kann durchaus sein, dass dieses schon nicht mehr existiert, wenn darauf zugegriffen wird. Ist jetzt aber nur eine Spekulation von mir, weil ich weiß ja nicht was Du in doSomething: so machst.

    Michael
  • Also ich greife dort auf jeden Fall auf das objekt zu.
    Aber auch wenn das objekt nicht mehr existieren würde, würde doch nicht alles einfrieren?
    Der Backgroundthread manipuliert quasi das Model etc. und der Mainthread ist halt für die GUI.
    Würde nur der backgroundthread einfrieren, wodurch auch immer, müsste der Mainthread ja trotzdem weiterlaufen.
    Durch die ganzen Logs die ich in meinem Programm habe kann ich auch relativ genau sehen, das das ganze Programm einfach stehen bleibt.
  • Das Objekt ist gar nicht mal so wichtig, wenn es null ist dann arbeitet der Mainthread mit dem weiter was er an Daten hat.
    Und wenn zB. gerade eine Animation ausgeführt, dürfte die ja nicht einfrieren.
    Ist ja absolut getrennt voneinander.
    Oder habe ich da einen denkfehler?
  • Erebos1988 schrieb:

    Als ich mit NSThreads anfing, wusste ich noch nichts von NSOperations.

    Kann jedem ja mal pasieren ;)

    Erebos1988 schrieb:

    Können die denn auch einen Timer aufnehmen?

    So wie Du den Timer im Thread verwendest, sieht das für mich erstmal nach doppelt-gemoppelt aus; insbesondere weil Du den Thread danach in seine Runloop schickst.

    Erebos1988 schrieb:

    Bieten NSOperations sonstige Vorteile?

    Sie sind eine Abstraktionsschicht und bieten eine Reihe von Möglichkeiten, die Du bei Threads selbst basteln musst: developer.apple.com/library/io…uid/TP40008091-CH101-SW14

    Aber wie gesagt: Meiner Meinung nach kannst Du auf den Thread verzichten und nur mir dem Timer arbeiten.
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:

    Aber wie gesagt: Meiner Meinung nach kannst Du auf den Thread verzichten und nur mir dem Timer arbeiten.
    Wie meinst du das, nur dem Timer nutzen?
    Der löst ja rechenintensive sachen aus, die würden dann doch die GUI zum stoppen bringen.
    Das hatte ich glaube in meiner ersten version so :P

    Oder meinst du der Timer soll dann jeweils eine Operation ausführen die ja im Hintergrund abläuft?
  • Amin Negm-Awad schrieb:

    Wenn der Timer rechenintensive Sachen auslöst, sollte das Ausgelöste in einem Thread, besser in einer Operation-Queue oder einem dispatch_async() stehen, nicht aber der Timer.
    So wie es momentan ist, laufen die rechenintensiven Sachen ja in einem extra Thread. Weil der Timer ja auch nicht im Mainthread läuft.
    Ich werde jetzt aber eh versuchen das mit einer Operation-Queue zu machen. Das Konzept sieht genau nach dem aus, was ich brauche/nutze
  • Anders herum: Es ist ja der Fehler, dass der Timer nicht im Mainthread läuft. Du kannst dir dann den gesamten Kram mit Runloops sparen in dem Thread oder $whatever einfach den Code herunterprogrammieren. Deine Frage, ob denn NSOperations auch Timer können entfällt ebenfalls: Sie müssen es nicht können.
    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"?
  • Nein, Timer müssen nicht immer im Mainthread laufen, aber sie benötigen eine runloop. Wenn Du eine NSOperationQueue statt threads verwendest, wanderst Du eine Abstraktionebene nach oben - was gut ist. Du musst dann nicht mehr mit threads hantieren, sondern übergibst der OQ einfach Arbeit (einen Block oder eine NSOperation-subklasse) und die Queue kümmert sich um die Verwaltung der threads.

    Den Timer kannst Du einfach im Mainthread laufen lassen (den gibt es eh und Du musst nicht an so Sachen wie eine eigene Runloop denken). In der Timermethode übergibst Du dann einfach wie schon von den anderen beschrieben die Operation an die Queue. Unter dem Strich sparst Du damit eine Menge Code und profitierst automatisch von Verbesserungen weiter unten im Framework.
  • Erebos1988 schrieb:

    Sollten Timer denn immer im Mainthread laufen?
    Im Hintergrundprozess sind bei mir ebenfalls Timer, die müsste ich ja dann auch auf dem Mainthread laufen lassen.

    Da werde ich dann doch an einigen Stellen umbauen müssen aber was muss das muss.

    Sollen, müssen, können, dürfen. Es macht die Sache einfacher, wenn sie im Mainthread laufen. Und in vielen Fällen ist es nicht sinnvoll, sie woanders laufen zu lassen, vor allem in deinem: Die Timermethode selbst blockiert ja nur ganz kurz, wenn sie lediglich einen Thead startet. Es gibt dann keinen Grund, sie selbst zu parallelisieren. Den Aufwand, den du dadurch bekommst, siehst du ja aktuell. Und du kannst ben jede mögliche Technologie nutzen, ohne zu fragen, ob sie "Timer kann" und was man selbst coden muss.

    Timer sind aus Nutzersicht eine Art der Parallelität, weil er nichts tun muss, sondern das Getimte "im Hintergrund" geschieht. Ein Timer im Thread ist dann einfach Parallelität in der Parallelität. Das ist nicht sinnvoll und schwieriger zu handhaben. Wozu?

    Sieh dazu mal meinen Vortrag:
    macoun.de/video2012gssa4.php
    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"?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Amin Negm-Awad ()