mein Distributed Object hat Schluckauf :-(

  • mein Distributed Object hat Schluckauf :-(

    Moin,

    In meinem Programm ist ein NSMutableArray mein Distributed Object, mit dem ich mehrere NSStrings vom Hauptthread in einen Subthread weiterreiche(n will).

    Ich habe damit leider noch keine Erfahrungen, aber mit Hilfe eines Beispieles habe ich folgenden Code geschrieben:

    Quellcode

    1. @interface Controller : NSObject
    2. {
    3. NSConnection *appkitConnection;
    4. NSConnection *serverConnection;
    5. NSPort *threadReceivePort;
    6. NSPort *threadSendPort;
    7. NSMutableArray *orderBuffer;
    8. }
    9. @end
    Alles anzeigen

    Quellcode

    1. char *newOrder = "";
    2. - (id)init
    3. {
    4. orderBuffer = [[NSMutableArray alloc] initWithCapacity:1];
    5. }
    6. - (void)threadStart
    7. {
    8. threadReceivePort = [[NSPort port] retain];
    9. threadSendPort = [[NSPort port] retain];
    10. appkitConnection = [[NSConnection alloc] initWithReceivePort:threadSendPort sendPort:threadReceivePort];
    11. [appkitConnection setRootObject:orderBuffer];
    12. [orderBuffer addObject:@"example text"]; // im orderBuffer werden nur NSStrings gesichert
    13. [NSThread detachNewThreadSelector:@selector(newThread) toTarget:self withObject:nil];
    14. }
    15. // Subthread
    16. - (void)newThread
    17. {
    18. NSAutoreleasePool *localPool;
    19. localPool = [[NSAutoreleasePool alloc] init];
    20. serverConnection = [NSConnection connectionWithReceivePort:threadReceivePort sendPort:threadSendPort];
    21. doSomething();
    22. [localPool release];
    23. }
    24. // irgendwann in doSomething() wird der orderBuffer überprüft, und gegebenenfalls holt sich newOrder das erste Objekt (einen NSString)
    25. - (void)getEingabe
    26. {
    27. id appkitProxy;
    28. appkitProxy = [serverConnection rootProxy];
    29. if ([appkitProxy count])
    30. {
    31. newOrder = (char *)[[appkitProxy objectAtIndex:0] UTF8String];
    32. }
    33. }
    Alles anzeigen



    getEingabe im Subthread überprüft, ob es neue Eingaben im orderBuffer gegeben hat, und speichert dann den ersten NSString aus dem NSMutableArray in newOrder.

    Leider funktioniert das nur in ca 99% aller Fälle: Hin und wieder, und (scheinbar) völlig willkürlich und nicht reproduzierbar, werden auf diese Weise Texte verstümmelt. Meistens fehlt der hintere Teil des Textes. Es ist aber auch schon passiert, dass nur noch unsichtbare Sonderzeichen übrig geblieben sind.


    Hat jemand eine Idee, woran das liegen könnte ? Was fehlt meinem DO, um mir ein zuverlässiger Helfer sein zu können ? :)

    Für Tips wirklich sehr dankbar,
    Lur
  • RE: mein Distributed Object hat Schluckauf :-(

    Das liegt einfach daran, dass dein programm zum selben Zeitpunkt zwei operationen an ein und dem selben objekt durchführt. Da kommt es durcheinander und es entsthen fehler. Das ist auch eines der zentralen Probleme bei Datenbanken, nur dass dort das problem noch viel heftiger auftritt (stell dir nur mal Amazon vor, mit zig millionen Abfragen / sekunde!!).

    In deinem Beispiel kann dir ein NSLock helfen. Wie das ganz genau geht kann ich dir leider nicht sagen, da ich bisher keine Threads hatte, die so einen Buffer hatten. Aber schau mal in dei Doku unter "Multithreading" oder "NSLock". Dort stehen mehrere Artikel, wie man das machen kann :)

    Max
  • RE: mein Distributed Object hat Schluckauf :-(

    Hmmm, ok. Ich hatte es bislang so verstanden, dass ein gleichzeitiger Zugriff beider Threads bei DOs eben nicht passieren kann... und dass NSLocks eine alternative Möglichkeit wären das Problem zu lösen, wenn man auf DOs verzichten möchte/muss.

    Ich hab das jetzt also mal getestet:
    Vor dem Aufruf des Subthreads einen Lock mit

    Quellcode

    1. theLock = [[[NSLock alloc] init] retain];
    initialisert, und dann alle Zugriffe auf orderBuffer mit

    Quellcode

    1. [theLock lock]
    2. // orderBuffer-zugriff
    3. [theLock unlock]
    gekapselt... leider ohne Erfolg. Die Probleme treten unverändert auf... ?

    Hat noch jemand eine Idee, was hier falsch laufen könnte ? Bneutze ich die NSLocks falsch ? Fehlt etwas anderes ?

    Dank und Gruss,
    Lur
  • RE: mein Distributed Object hat Schluckauf :-(

    Nö, eigentlich mache ich das nicht immer so... nur wenn ich nachts um 1 noch irgenwelche Tests mache, und mein Hirn schon schlafen ist. Denn auch wenn dies mein erstes Cocoa-Prg ist ... DAS hatte ich eigentlich schon gelernt und verstanden :)

    Danke,
    Lur
  • RE: mein Distributed Object hat Schluckauf :-(

    Danke erstmal für die Links ... ich hab die jetzt ca 5 mal durchgelesen, und ich meine im Grossteil auch verstanden. Leider habe ich damit mein Problem trotzdem noch nicht lösen können.

    Ich hab jetzt folgendes als Notlösung gemacht:
    - Der MainThread bastelt an jeden String hinten ein Sonderzeichen ran, bevor er es in das DO legt.
    - Der SubThread überprüft ob das Sonderzeichen da ist (also der String komplett ist), wenn nicht, dann verwirft er ihn, und holt ihn erneut aus dem DO.

    Klingt kompliziert, geht aber... zwar hasse ich eigentlich solche Notlösungen, aber was hilfts ... Irgendwann, bei gestiegener Kompetenz, werde ich mich dem noch mal annehmen ... :D

    Danke nochmals,
    Lur
  • RE: mein Distributed Object hat Schluckauf :-(

    Original von lurchi13

    getEingabe im Subthread überprüft, ob es neue Eingaben im orderBuffer gegeben hat, und speichert dann den ersten NSString aus dem NSMutableArray in newOrder.

    Leider funktioniert das nur in ca 99% aller Fälle: Hin und wieder, und (scheinbar) völlig willkürlich und nicht reproduzierbar, werden auf diese Weise Texte verstümmelt. Meistens fehlt der hintere Teil des Textes.


    Frage: wie wird eigentlich sichergestellt, dass nicht 2x ein addObject auf den Puffer stattfindet bis es ausgelesen wird? D.h. es findet offenbar KEIN Handshaking statt. Und es wird ggf. mehrmals geschrieben bis - irgendwann - der andere Thread mal feststellt dass etwas da ist. Und dann liest er nur den ersten Eintrag.

    Nochwas zu den Interna von DO: im Subtread bekommst Du einen Proxy, der alle Methodenaufrufe (z.B. count) an das Original im anderen Thread weiterreicht. Wann das passiert ist nirgends garantiert. Vermutlich aber während der RunLoop im Haupthread. Das heisst dass der Hauptthread zwischen "if count" und "objectAtIndex:" im Subthread eine ganze Menge andere Dinge machen kann (z.B. Autorelease-Pool leeren).

    Das erklärt vielleicht warum Teile fehlen aber nicht das mit den Sonderzeichen. Dazu die Frage, wer das Objekt eigentlich aus dem Puffer-Array löscht? Der Hauptthread oder der Subthread?

    Noch eine andere Idee: probiere es mal mit NSPipe. Das ist ein echter FIFO der auch ggf. blockiert wenn er beim Schreiben voll läuft bzw. beim Lesen leer ist. Und intern ist das eine POSIX pipe(), d.h. man kann es per fdopen() in einen FILE * wandeln, und die normalen getchar(), getc(), scanf() drauf verwenden. So wie es aussieht brauchst Du ja sowieso einen UTF-8-String und keinen NSString und den kannst Du im Haputthread gleich als UTF8 schreiben.

    -- hns
  • RE: mein Distributed Object hat Schluckauf :-(

    Soll ich ehrlich sein ? Ich habe etwa jedes 2. Wort verstanden ... Ich fürchte da kommt noch VIEEEEL Lesezeit auf mich zu...

    Um das Gesamtproblem etwas leicher verstehen zu können, kannst Du mal hier schauen:
    osxentwicklerforum.de/thread.php?threadid=1455

    Das war eigentlich mein Ausgangsproblem... welches ich bislang auf diese Weise hier gelöst habe... eine Schleife im SubThread, die ununterbrochen den Buffer überprüft, und immer den ersten Eintrag abarbeitet. (der abgearbeitete Befehl wird per "performSelectorOnMainThread" im MainThread gelöscht)
    Somit ist es egal, wie oft dort vom MainThread etwas neues hinzugefügt wird...

    Wenn ich es richtig verstanden habe könnte NSPipe ein Ansatz sein, mein Gesamtproblem eleganter zu lösen ? Jetzt weiss ich wenigstens, was ich am Wochenende mache ... :)

    Vielen Dank,
    Lur
  • RE: mein Distributed Object hat Schluckauf :-(

    ununterbrochen den Buffer überprüft, und immer den ersten Eintrag abarbeitet.

    Threads werden unterbrochen - und zwar durch andere Threads. Deshalb reicht es m.E. nicht einfach den ersten Eintrag abzuarbeiten - weil es mehr als einen geben kann. Was evtl. hilft ist ALLE im Puffer vorhandenen abzuarbeiten.
    Wenn ich es richtig verstanden habe könnte NSPipe ein Ansatz sein, mein Gesamtproblem eleganter zu lösen ?

    Ich habe nochmal in den Hauptdiskussion geschaut - da steht ja drin, dass getchar() verwendet wird. Damit sollte in etwa Folgendes den Anfang bilden:

    Quellcode

    1. im Hauptthread:
    2. NSPipe *p=[NSPipe pipe];
    3. // subthread erzeugen und p übergeben
    4. [[p fileHandleForWriting] writeData:[@"teststring\n" dataUsingEncoding:NSUFT8StringEncoding]]
    5. ...
    6. [[p fileHandleForWriting] closeFile]; // sollte den Rest rausschreiben und im Subthread EOF erzeugen
    7. im Subthread:
    8. dup2([p fileHandleForReading] fileDescriptor], 0); // POSIX: stdin umlenken auf Pipe
    9. ...
    10. while((c=getchar()) != EOF)
    11. ...
    12. [[p fileHandleForReading] closeFile];
    Alles anzeigen


    -- hns
  • RE: mein Distributed Object hat Schluckauf :-(

    WOW ! Das hat mich wirklich weiter gebracht! ... Es wird zwar noch etwas dauern, bis ich das Programm komplett darauf angepasst und es laufen habe, aber erste Tests waren eben schon sehr erfolgreich.

    Vielen, vielen Dank !!

    Gruss,
    Lur