Welchen Vorteil(E) hat dispatch_sync haben über @synchronisiert?
Können sagen, ich möchte diese code thread-safe:
- (void) addThing:(id)thing { //Can be called from different threads
[_myArray addObject:thing];
}
GCD scheint der bevorzugte Weg, dies zu erreichen:
- (void) addThing:(id)thing {
dispatch_sync(_myQueue, ^{ //_myQueue is serial.
[_myArray addObject:thing];
});
}
Welchen Vorteil(E) hat es über die traditionelle Methode?
- (void) addThing:(id)thing {
@synchronized(_myArray) {
[_myArray addObject:thing];
}
}
- "GCD Nähte wie die bevorzugte Art und Weise" ... haben Sie eine Referenz für diese Aussage?
- Ich habe das Gefühl, von der GCD WWDCs, dass dies die moderne Art und Weise. Ich könnte falsch sein, tho.
- die meisten der Innovationen sind BESSER...nicht immer!!!
- Ok, ich glaube, ich fand einen Verweis. @synchronisierte verwendet sperren unter der Haube, richtig? "Warteschlangen nicht zu verhängen, die gleichen Strafen wie sperren. Zum Beispiel, queueing ' - eine Aufgabe, die nicht erfordert überfüllung in den kernel zu erwerben, eine mutex." developer.apple.com/library/ios/documentation/General/...
- Nicht haben Sie ganz andere Strategien? Sie können nicht dispatch_sync in einem rekursiven Aufruf auf dem gleichen Warteschlange, Sie wird deadlock,
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wow. OK-Meine ursprüngliche Bewertung der Leistung war flach aus falsch. Farbe mich dumm.Nicht so dumm. Mein performance-test falsch war. Behoben. Zusammen mit einem "deep dive" in den GCD-code.
Update: Code für die benchmark finden Sie hier: https://github.com/bbum/StackOverflow Hoffentlich ist es jetzt korrekt. 🙂
Update2: es wurde ein 10-queue-version, die von jeder Art von test.
OK. Umschreiben die Antwort:
•
@synchronized()
gewesen herum für eine lange Zeit. Es ist implementiert als hash-lookup zu finden, eine Sperre, die ist dann gesperrt. Es ist "ziemlich schnell" - in der Regel schnell genug-aber auch eine Last sein kann, die unter hohem Streit (wie jede Synchronisations-primitive).•
dispatch_sync()
nicht unbedingt ein Schloss, noch ist es erforderlich den block kopiert werden. Insbesondere in der fastpath Fall, diedispatch_sync()
wird, rufen Sie den block direkt auf dem aufrufenden thread, ohne Sie zu kopieren den block. Auch in der slowpath Fall, wird der Baustein nicht kopiert werden, da der aufrufende thread blockiert, bis die Ausführung sowieso (der aufrufende thread angehalten, bis alles, was Arbeit ist vor derdispatch_sync()
fertig ist, dann wird der Faden wieder aufgenommen wird). Die einzige Ausnahme ist der Aufruf auf das Haupt-queue/thread; in diesem Fall wird der block immer noch nicht kopiert (weil der aufrufende thread angehalten und, wenn Sie also einen block aus dem Stapel ist OK), aber es ist ein Haufen Arbeit das einreihen auf der main queue, ausführen und dann wieder dem aufrufenden thread.•
dispatch_async()
erforderlich, dass der block kopiert werden, wie es nicht ausführen von auf dem aktuellen thread noch kann der aktuelle thread blockiert (da der block kann sofort sperren auf einige thread-lokale Ressource, die nur auf die Zeile des Codes nach derdispatch_async()
. Während teuer,dispatch_async()
bewegt sich die Arbeit aus dem aktuellen thread, so dass es zur Wiederaufnahme der Ausführung sofort.Endergebnis --
dispatch_sync()
ist schneller als@synchronized
, aber nicht durch einen allgemein sinnvollen Betrag (auf eine '12-iMac, noch '11 mac mini -- #s zwischen den beiden sind sehr unterschiedlich, btw... die Freuden der Parallelität). Mitdispatch_async()
ist langsamer als beide in der uncontended Fall, aber nicht viel. Jedoch, die Verwendung von 'dispatch_async()' ist deutlich schneller, wenn die Ressource, die unter Konflikten.Nehmen Sie die oben mit einem Körnchen Salz; es ist ein micro-benchmark der schlimmsten Art, dass es nicht die Darstellung einer wirklichen Welt gemeinsame Nutzung Muster. Die "unit of work" ist wie folgt, und die Ausführungszeiten der oben darstellen von 1.000.000 Ausführungen.
(_c wird auf 0 zurückgesetzt, am Anfang von jedem Durchgang, und behauptet, um == zu der Anzahl der Testfälle am Ende, um sicherzustellen, dass der code tatsächlich die Ausführung aller arbeiten, bevor spuckt der Zeit.)
Für die uncontended Fall:
Für die Behauptung aufgestellt, 2 queue, Fall (q1 und q2 sind Seriell):
Oben werden einfach wiederholt, für jede work unit Variante (keine tricksy runtime-y-magic im Einsatz; copypasta FTW!!!).
In diesem Sinne:
• Verwenden Sie
@synchronized()
wenn Sie mögen, wie es aussieht. Die Realität ist, dass, wenn Ihr code wird behauptet, auf dem array, ist wahrscheinlich ein Architektur-Problem. Hinweis: mit@synchronized(someObject)
kann unbeabsichtigte Folgen haben, dass es kann dazu führen, weitere Konflikte, wenn das Objekt intern verwendet@synchronized(self)
!• Verwenden Sie
dispatch_sync()
mit einer seriellen queue, wenn das ist Ihre Sache. Es gibt keine overhead-es ist eigentlich schneller, sowohl in der Schalt-und uncontended Fall ist -- und mit queues sind leichter zu Debuggen und einfacher zu Profil, der Instrumente und der Debugger haben beide ausgezeichnete tools für debugging-Warteschlangen (und Sie sind immer besser die ganze Zeit) in der Erwägung, dass die debugging-sperren können ein Schmerz sein.• Verwenden Sie
dispatch_async()
mit unveränderlichen Daten für die stark umkämpften Ressourcen. I. e.:Schließlich sollte es eigentlich egal, welche Sie verwenden, für die Aufrechterhaltung, den Inhalt einer array. Die Kosten des Streites hat, ist außerordentlich hoch für den synchronen Fall. Für den asynchronen Fall werden die Kosten von Konflikten geht den Weg nach unten, aber das potential für Komplexität ist komisch oder performance-Fragen geht Weg.
Beim entwerfen Auger Systeme ist es am besten, um die Grenze zwischen Warteschlangen so gering wie möglich. Ein großer Teil davon ist, sicherzustellen, dass so wenig Ressourcen wie möglich "live" auf beiden Seiten der Grenze.
thing = [thing copy];
auf der @synchronisierte Weise zu?dispatch_sync()
kein anderes thread, wenn es nicht zu: gist.github.com/woolsweater/5882484, die ich nicht vollständig wissen, was "nicht" bedeutet, aber libdispatch ist open source, wenn Sie Pflege zu suchen. In meiner kleinen demo, der ursprüngliche thread hat zu warten, bis das Ergebnis der operation, egal was, so Schalt scheint unnötig.addThing:
Sie mitdispatch_async
-- nichtdispatch_sync
. Ist dies Zufall? Ein weiteres follow-up: nehmen wir an, die sync-Warteschlange ist nicht stark behauptete und der eigentliche code ausgeführt wird, ist sehr klein (im Grunde eine Zuweisung). Würden Sie vorschlagen, zu verwenden Versand sync über async -- die weitere Gefahr für dead locks, in case of sync verwaltet werden können?Habe ich festgestellt, dass dispatch_sync() ist ein schlechter Weg, um zu tun, sperren, er unterstützt keine verschachtelten Aufrufe.
So kann man das auch nicht nennen dispatch_sync auf eine serielle Q und dann nennen Sie es noch einmal in einem Unterprogramm mit der gleichen Frage: Welches bedeutet, dass es nicht in der gleichen Weise Verhalten wie @synchronisiert nicht an alle.
Ok,
Ich habe getan, einige weitere tests, und hier sind die Ergebnisse:
lock-test: mean:2.48661, stdDev:0.50599
synchronisiert-test: mean:2.51298, stdDev:0.49814
Versand Test: mean:2.17046, stdDev:0.43199
So, ich war falsch, mein Fehler 🙁
Wenn jemand interessiert ist, in der test-code ist es Erfolg hier:
Gibt es ein paar Sachen:
1) @Synchronisieren ist die schwere Ausführung von sperren auf einige monitor (ich persönlich bevorzuge NSLock/NSRecursiveLock)
2) Dispatch_sync baut Ausführung Warteschlange.
sowohl Ansatz führt zu ähnlichen Ergebnis in Ihrem Fall aber für solch einfache Lösung wie collection threadsicher ist, würde ich lieber 1.
Warum:
Wenn Sie haben mehrere Kerne, dann werden auch mehrere threads können gleichzeitig arbeiten. Je nach scheduler, die Sie sperren, für sehr kurze Zeit auf dem monitor.
es ist viel leichter, die Zuweisung eines neuen Blocks, Beibehaltung 'Ding' setzen in der Warteschlange (das ist auch der thread synchronisiert), und die Ausführung beim arbeiten queue bereit ist.
sowohl Ansatz die Reihenfolge der Ausführung wird sehr unterschiedlich sein.
Wenn an einem gewissen Punkt werden Sie entdecken, starke Nutzung der Sammlung können Sie prüfen, ändern, sperren, Lesen/Schreiben Typ das ist viel einfacher, umgestalten/verändern, wenn Sie mit einigen NSLock-wie Klasse statt sync_queue.