6

Ich habe eine Objective-C Klasse mit einigen Verfahren, die eine GCD Warteschlange verwenden, die gleichzeitigen Zugriffe auf eine Ressource erfolgen, um sicherzustellen, seriell (standard Art und Weise, dies zu tun).Wie implementiert man einen Wiedereintrittssperrmechanismus in Ziel-c durch GCD?

Einige dieser Methoden müssen andere Methoden der gleichen Klasse nennen. Also muss der Verriegelungsmechanismus einspringend sein. Gibt es einen Standard Weg dies zu tun?

Zunächst musste ich jede dieser Methoden verwenden

dispatch_sync(my_queue, ^{ 

    // Critical section 

}); 

Zugriffe zu synchronisieren. Wie Sie wissen, tritt eine Deadlock auf, wenn eine dieser Methoden eine andere Methode aufruft, da der Aufruf dispatch_sync die aktuelle Ausführung stoppt, bis dieser andere Block ausgeführt wird, der auch nicht ausgeführt werden kann, weil die Ausführung in der Warteschlange gestoppt wird. Um dies zu lösen, habe ich dann z.B. Diese Methode:

- (void) executeOnQueueSync:(dispatch_queue_t)queue : (void (^)(void))theBlock { 
    if (dispatch_get_current_queue() == queue) { 
     theBlock(); 
    } else { 
     dispatch_sync(queue, theBlock); 
    } 
} 

Und in jedem meiner Methoden, verwende ich

[self executeOnQueueSync:my_queue : ^{ 

    // Critical section 

}]; 

ich diese Lösung nicht gefällt, weil für jeden Block mit einem anderen Rückgabetyp, muss ich eine andere Methode schreiben. Darüber hinaus scheint dieses Problem sehr häufig zu sein und ich denke, dass es eine schönere Standardlösung dafür geben sollte.

+0

Haben Sie erwogen, stattdessen '@ synchronized' zu verwenden? –

+1

@MartinR, ja, aber '@ synchronized' ist eine klassische Sperre, die nicht auf GCD/Queues basiert, und aus diesem Grund ist es aus Gründen der Einfachheit und Leistung des Codes nicht ratsam, sie zu verwenden. Der Titel dieser Frage ist daher irreführend, weil er "lock" enthält. Was ich meine, ist die Synchronisierung des Zugriffs in einer reentranten Weise mit GDC/Warteschlangen. Ich hatte einfach keine besseren Worte als "Reentrant Locking" zu sagen, denn das ist der Name der Lösung für das Problem, die die meisten Leute kennen. –

+1

@MartinR, obwohl '' synchronisiert ''hat den Vorteil, zu einfacheren Code für Reentrant -was- (wie man das am besten nennen?) Zu führen. Dennoch hat der Sperrmechanismus hinter ihm weniger Leistung als der Mechanismus hinter GCD-Warteschlangen. –

Antwort

10

Das Wichtigste zuerst: dispatch_get_current_queue() ist veraltet. Der kanonische Ansatz wäre nun, dispatch_queue_set_specific zu verwenden. Ein solches Beispiel könnte wie folgt aussehen:

typedef dispatch_queue_t dispatch_recursive_queue_t; 
static const void * const RecursiveKey = (const void*)&RecursiveKey; 

dispatch_recursive_queue_t dispatch_queue_create_recursive_serial(const char * name) 
{ 
    dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL); 
    dispatch_queue_set_specific(queue, RecursiveKey, (__bridge void *)(queue), NULL); 
    return queue; 
} 

void dispatch_sync_recursive(dispatch_recursive_queue_t queue, dispatch_block_t block) 
{ 
    if (dispatch_get_specific(RecursiveKey) == (__bridge void *)(queue)) 
     block(); 
    else 
     dispatch_sync(queue, block); 
} 

Dieses Muster durchaus brauchbar ist, aber es ist wohl nicht kugelsicher, weil Sie verschachtelte rekursive Warteschlangen mit dispatch_set_target_queue schaffen könnte, und versuchen, die Arbeit an der äußeren Warteschlange einzureihen aus dem Inneren der innere Deadlock würde, auch wenn Sie bereits „innerhalb des Schloss“ sind (in Hohn zitiert, weil es nur wie ein Schloss aussieht, ist es eigentlich etwas anderes: eine Warteschlange - also die Frage, nicht wahr?) für die äußere. (Sie könnten um das zu erhalten, indem Anrufe dispatch_set_target_queue Einwickeln und Pflege Ihrer eigenen Out-of-Band-Graph-Targeting, etc., aber das ist als eine Übung für den Leser überlassen.)

Sie gehen zu sagen:

ich mag diese Lösung nicht, weil für jeden Block mit einem anderen Rückgabetypen, ich eine andere Methode schreiben müssen.

Die allgemeine Idee dieser "state-protecting serial queue" -Muster ist, dass Sie privaten Staat schützen; Warum würdest du "deine eigene Warteschlange" dazu bringen? Wenn es sich um mehrere Objekte handelt, die den Statusschutz gemeinsam nutzen, geben Sie ihnen eine inhärente Möglichkeit, die Warteschlange zu finden (d. H. Sie entweder zur Initialisierungszeit einschieben oder sie für alle Beteiligten zugänglich machen). Es ist nicht klar, wie es hier nützlich wäre, eine eigene Warteschlange zu erstellen.

+2

Danke für die Erklärung über die veraltete Funktion und was stattdessen zu verwenden, und wie. Das allein ist viel wert. –

+0

@ipmcc, ich bin sehr beeindruckt von Ihren Antworten und Kommentaren zu diesen und verwandten Themen. Könnten Sie sich bitte auch diese [Frage] (http://stackoverflow.com/questions/20201078/how-to-implement-a-reentrant-locking-mechanism-through-dispatch-concurrent-queue) ansehen, die ich gerade gepostet habe ? –

+0

@ipmcc Gibt es einen Unterschied in der Effektivität oder Effizienz mit diesem Ansatz im Gegensatz zu einer Warteschlange mit einem Label & 'dispatch_queue_get_label (DISPATCH_CURRENT_QUEUE_LABEL)'? – Orangenhain

Verwandte Themen