Frage

Ich versuche, die beste Art und Weise in meinem Kopf zu arbeiten, eine Cocoa-Anwendung zu strukturieren, die im Wesentlichen ist ein gleichzeitiger Download-Manager. Es gibt einen Server die App im Gespräch mit den Benutzern eine große Liste von Dingen machen nach unten zu ziehen und die App Prozesse, die Liste. (Es ist nicht HTTP oder FTP, also kann ich nicht das URL-Ladesystem verwenden;. Ich werde über Socket-Verbindungen sprechen)

Dies ist im Grunde die klassischen Erzeuger-Verbraucher-Muster. Der Trick besteht darin, dass die Zahl der Verbraucher festgelegt ist, und sie sind hartnäckig. Der Server setzt eine strenge Begrenzung für die Anzahl der gleichzeitigen Verbindungen, die geöffnet werden können (wenn auch in der Regel mindestens zwei), und neue Verbindungen zu öffnen teuer ist, so in einer idealen Welt, die gleichen N-Verbindungen für die gesamte Lebensdauer des App geöffnet sind.

Eine Möglichkeit, dies zu nähern könnte sein, N Threads zu erstellen, von denen jeder würde „besitzen“, um eine Verbindung, und warten Sie auf der Anforderungswarteschlange, Sperrung, wenn es leer ist. Da die Anzahl der Verbindungen wird nie sehr groß sein, ist dies im Hinblick auf dem tatsächlichen System-Overhead nicht unvernünftig. Aber konzeptionell, wie es scheint, Cocoa muss eine elegantere Lösung.

Es scheint, wie ich könnte ein NSOperationQueue und Anruf setMaxConcurrentOperationCount: mit der Anzahl der Verbindungen. Dann werfe ich nur auf die Download-Anfragen in diese Warteschlange. Aber ich bin mir nicht sicher, in diesem Fall, wie die Verbindungen verwalten sich selbst. (Setzen Sie sie einfach auf einem Stapel, und verlassen sich auf die Warteschlange ich nicht über, um sicherzustellen, / Unterschreitungs? Wurf in einem Dispatch Semaphore mit dem Stapel zusammen?)

Nun, da wir in der schönen neuen Welt sind von Grand Central Dispatch , ist, dass jede andere Möglichkeiten eröffnen diese anzugehen? Auf den ersten Blick scheint es nicht, wie es da Flaggschiff Fähigkeit des GCD dynamisch Parallelität zu skalieren (und in Apples Empfehlungen auf Ändern Producer-Consumer Implementations ) gefällt mir nicht wirklich helfen. Aber ich habe kratzte nur an der Oberfläche über sie zu lesen.

EDIT:

Falls es wichtig ist: ja, ich bin die Planung des asynchronen / nicht-blockierenden Socket-APIs zur Verwendung der tatsächliche Kommunikation mit dem Server zu tun. So ist die I / O selbst nicht auf einem eigenen Thread sein (s). Ich bin nur besorgt mit der Mechanik der Arbeit Schlange stehen, und (sicher) es aus den Verbindungen doling, sobald sie verfügbar sind.

War es hilfreich?

Lösung 2

Für die Nachwelt willen, nach einiger Diskussion an anderer Stelle, die Lösung, die ich denke, dass ich für dies annehmen würde, ist im Grunde:

  • Haben Sie eine Warteschlange anhängiger Download-Vorgänge, zunächst leer.
  • Haben Sie einen Satz alle offenen Verbindungen enthält, zunächst leer.
  • Haben Sie einen änderbaren Array (Queue, wirklich) von Leerlauf offenen Verbindungen, zunächst leer.
  • Wenn der Benutzer eine Download-Anforderung ergänzt:
    • Wenn das Array von Verbindungen im Leerlauf nicht leer ist, entfernen Sie ein und weisen Sie den Download zu.
    • Wenn es keine Verbindungen im Leerlauf, aber die Anzahl der gesamten Verbindungen hat seine Grenze nicht erreicht, eine neue Verbindung öffnen, fügen Sie es den Satz, und weisen Sie den Download es.
    • Andernfalls einreihen den Download für später.
  • Wenn ein Download abgeschlossen ist: Wenn es Anforderungen in der Warteschlange, dequeue ein und geben Sie es an den Anschluss; andernfalls die Verbindung zum Ruheliste hinzuzufügen.

All diese Arbeit findet am Hauptfaden. Die Arbeit der Decodierung der Ergebnisse jeder Download würde GCD abgeladen werden, so kann damit umgehen die Gleichzeitigkeit Drosselung, und es nicht den Haupt-Thread verstopfen.

Öffnen eine neue Verbindung könnte eine Weile dauern, so dass der Prozess einen neuen zu erstellen vielleicht ein bisschen komplizierter in der Praxis sein (zum Beispiel einreihen den Download initiieren den Verbindungsprozess und dequeue es dann, wenn die Verbindung vollständig ist etabliert). Aber ich denke immer noch, meine Wahrnehmung der Möglichkeit der Rennbedingungen zu hoch angesetzt wurde.

Andere Tipps

Wenn Sie CFSocket nicht-blockierende Anrufe verwenden für I / O, ich bin einverstanden, dass alle auf dem Hauptthread geschehen soll, lassen die OS die Parallelitätsprobleme behandeln, da Sie nur Daten kopiert und nicht wirklich tun Berechnung.

Darüber hinaus, es klingt wie die einzige andere Arbeit, um Ihre App zu tun brauchen, ist eine Warteschlange von Elementen halten heruntergeladen werden. Wenn eine der Übertragungen abgeschlossen ist, kann der CFSocket Rückruf in der Warteschlange die Übertragung des nächsten Elements initiieren. (Wenn die Warteschlange leer ist, verringern Sie Ihre Verbindungsanzahl, und wenn etwas zu einer leeren Warteschlange hinzugefügt wird, eine neue Übertragung starten.) Ich sehe nicht, warum Sie mehrere Threads dafür brauchen.

Vielleicht haben Sie links etwas wichtiges, aber auf der Grundlage Ihrer Beschreibung ist die App I / O Bindung nicht CPU gebunden ist, würde, so dass alle von der Gleichzeitigkeit Zeug nur komplizierter Code mit minimalen Auswirkungen auf die Leistung machen.

Haben sie alle auf dem Haupt-Thread.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top