Sémaphores et P, V, Z, R

ChachaChacha Membre
septembre 2007 modifié dans Objective-C, Swift, C, C++ #1
Salut,

Le multi-thread est à  la mode, et il ne va pas se démoder avant un moment. Vous avez eu (ou pas) des cours de gestion de ressources, et vous savez qu'il faut protéger son code des accès concurrents. @synchronized, NSLock et NSConditionLock sont là  pour ça, mais on ne peut pas tout faire... notamment, un sémaphore avec compteur.
Je rappelle le principe :
Ce sémaphore représente des ressources, il contient un entier donnant le nombre de ressources disponibles. On peut prendre une ressource (opérateur P), ce qui diminue le compteur; on peut libérer une ressource (opérateur V).
l'opérateur P est bloquant, c'est-à -dire que si le nombre de ressources est à  0, le thread est bloqué; un autre thread doit faire V sur le sémaphore.
L'opération Z, c'est un peu l'inverse : bloqué tant que le sémaphore n'est pas à  0
R, c'est juste pour connaà®tre le nombre de ressources (pas très utile, sauf des fois)

Je ne vous refais pas un cours, vous trouverez facilement des problèmes de consommateur/producteur, coiffeurs, trains de Bolivie, etc.

Par contre, je peux vous proposer une implémentation pour Cocoa de ce sémaphore :

<br />//<br />//&nbsp; Semaphore.h<br />//&nbsp; MozoDojo<br />//<br />//&nbsp; Created by Pierre Chatelier on 09/10/06.<br />//&nbsp; Copyright 2005, 2006, 2007 Pierre Chatelier. All rights reserved.<br />//<br /><br />#import &lt;Cocoa/Cocoa.h&gt;<br /><br />#include &lt;pthread.h&gt;<br /><br />@interface Semaphore : NSObject &lt;NSCoding&gt; {<br />&nbsp; pthread_condattr_t&nbsp; cond_attr;<br />&nbsp; pthread_cond_t&nbsp; &nbsp; &nbsp; cond;<br />&nbsp; pthread_mutexattr_t mutex_attr;<br />&nbsp; pthread_mutex_t&nbsp; &nbsp; &nbsp;mutex;<br />&nbsp; volatile int*&nbsp; &nbsp; &nbsp; &nbsp;value;<br />}<br /><br />-(id) initWithValue:(int)initialValue; //designated initializer<br />-(id) init;//init with value 0<br /><br />-(void) P:(int)n;<br />-(void) V:(int)n;<br />-(void) P;//P with 1<br />-(void) V;//V with 1<br />-(int)&nbsp; R;<br />-(void) Z;<br /><br />//NSCoding protocol<br />-(id)&nbsp; &nbsp;initWithCoder:(NSCoder*)coder;<br />-(void) encodeWithCoder:(NSCoder*)coder;<br /><br />@end<br />


<br />//<br />//&nbsp; Semaphore.m<br />//&nbsp; MozoDojo<br />//<br />//&nbsp; Created by Pierre Chatelier on 09/10/06.<br />//&nbsp; Copyright 2005, 2006, 2007 Pierre Chatelier. All rights reserved.<br />//<br /><br />#import &quot;Semaphore.h&quot;<br /><br />@implementation Semaphore<br /><br />//designated initializer<br />-(id) initWithValue:(int)initialValue<br />{<br />&nbsp; if (![super init])<br />&nbsp; &nbsp; return nil;<br />&nbsp; int error = pthread_condattr_init(&amp;cond_attr);<br />&nbsp; error = error ? error : pthread_cond_init(&amp;cond, &amp;cond_attr);<br />&nbsp; error = error ? error : pthread_mutexattr_init(&amp;mutex_attr);<br />&nbsp; error = error ? error : pthread_mutex_init(&amp;mutex, &amp;mutex_attr);<br />&nbsp; value = calloc(1, sizeof(int));<br />&nbsp; error = error ? error : (!value);<br />&nbsp; if (value)<br />&nbsp; &nbsp; *value = initialValue;<br />&nbsp; if (error)<br />&nbsp; {<br />&nbsp; &nbsp; [self autorelease];<br />&nbsp; &nbsp; return nil;<br />&nbsp; }<br /><br />&nbsp; return self;<br />}<br />//end initWithValue:<br /><br />-(id) init<br />{<br />&nbsp; return [self initWithValue:0];<br />}<br />//end init<br /><br />-(void) dealloc<br />{<br />&nbsp; pthread_condattr_destroy(&amp;cond_attr);<br />&nbsp; pthread_cond_destroy(&amp;cond);<br />&nbsp; pthread_mutexattr_destroy(&amp;mutex_attr);<br />&nbsp; pthread_mutex_destroy(&amp;mutex);<br />&nbsp; free((void*)value);<br />&nbsp; [super dealloc];<br />}<br />//end dealloc<br /><br />-(void) P:(int)n<br />{<br />&nbsp; pthread_mutex_lock(&amp;mutex);<br />&nbsp; while(*value&lt;n)<br />&nbsp; &nbsp; pthread_cond_wait(&amp;cond, &amp;mutex);<br />&nbsp; *value -= n;<br />&nbsp; pthread_mutex_unlock(&amp;mutex);<br />&nbsp; pthread_cond_broadcast(&amp;cond);<br />}<br />//end P:<br /><br />-(void) P<br />{<br />&nbsp; [self P:1];<br />}<br />//end P<br /><br />-(void) V:(int)n<br />{<br />&nbsp; pthread_mutex_lock(&amp;mutex);<br />&nbsp; *value += n;<br />&nbsp; pthread_mutex_unlock(&amp;mutex);<br />&nbsp; pthread_cond_broadcast(&amp;cond);<br />}<br />//end V:<br /><br />-(void) V<br />{<br />&nbsp; [self V:1];<br />}<br />//end V<br /><br />-(int) R<br />{<br />&nbsp; return *value;<br />}<br />//end R<br /><br />-(void) Z<br />{<br />&nbsp; pthread_mutex_lock(&amp;mutex);<br />&nbsp; while(*value)<br />&nbsp; &nbsp; pthread_cond_wait(&amp;cond, &amp;mutex);<br />&nbsp; pthread_mutex_unlock(&amp;mutex);<br />}<br />//end Z<br /><br />//NSCoding protocol<br />-(id) initWithCoder:(NSCoder*)coder<br />{<br />&nbsp; return [self initWithValue:[coder decodeIntForKey:@&quot;value&quot;]];<br />}<br /><br />-(void) encodeWithCoder:(NSCoder*)coder<br />{<br />&nbsp; [coder encodeInt:[self R] forKey:@&quot;value&quot;];<br />}<br /><br />@end<br />


Et à  ceux qui se demandent pourquoi j'ai utilisé les pthread_cond_wait au lieu des fonctions semop, c'est parce que les semop encapsulent des short, et que 32768 c'est un peu limite parfois dans un consommateur/producteur.
Vous remarquerez que j'ai fait du C, je n'ai pas introduit de NSLock ou @synchronized.
Et si vous avez des améliorations à  proposer... pas de prob' !

A+

Chacha

[edit]
Bon, déjà  on pourrait utiliser un unsigned pour le compteur; et sinon, je croyais que volatile ne s'appliquait qu'à  un pointeur, mais en fait il semblerait que non

Réponses

  • psychoh13psychoh13 Mothership Developer Membre
    09:39 modifié #2
    Je te conseille deux trucs :

    Pour commencer :
    if (![super init])<br />&nbsp; &nbsp; return self;
    


    Ici retourne "nil" plutôt que self, car comme tu ne modifies pas la valeur de self, si l'initialisation de la classe supérieur retourne nil, self a de fortes chances d'avoir une valeur toute pourrie.

    Ensuite, au lieu d'utiliser les fonctions calloc() et free(), je te conseille d'utiliser les méthodes NSZoneCalloc() et NSZoneFree(), ça permettra à  tes données d'être conservées dans le même espace que ton objet, si d'aventure quelqu'un l'utilisait avec des zones. C'est juste deux petites modifications :

    value = NSZoneCalloc([self zone], 1, sizeof(int));<br />NSZoneFree([self zone], value);
    


    De plus, en C, le cast dans free() vers un void* n'est pas utile, pas besoin d'écrire free((void*)value) tu peux écrire directement free(value).

    Pour finir, il n'est pas nécessaire de réécrire dans l'interface le nom des méthodes des protocoles que tu utilises, tu peux t'en passer.
  • ChachaChacha Membre
    09:39 modifié #3
    dans 1190797145:

    if (![super init])<br />&nbsp; &nbsp; return self;
    

    Ici retourne "nil" plutôt que self

    Ah, oui, exact, c'est une coquille. Merci


    Ensuite, au lieu d'utiliser les fonctions calloc() et free(), je te conseille d'utiliser les méthodes NSZoneCalloc() et NSZoneFree(), ça permettra à  tes données d'être conservées dans le même espace que ton objet, si d'aventure quelqu'un l'utilisait avec des zones.

    Tiens, je ne les connaissais pas, celle-là .



    De plus, en C, le cast dans free() vers un void* n'est pas utile, pas besoin d'écrire free((void*)value) tu peux écrire directement free(value).

    Oui, c'est juste pour éviter un warning, quand, comme moi, on aime à  en activer un maximum.


    Pour finir, il n'est pas nécessaire de réécrire dans l'interface le nom des méthodes des protocoles que tu utilises, tu peux t'en passer.

    Non, ça n'est pas nécessaire, mais j'aime bien comme aide-mémoire et auto-documentation (même si, je suis d'accord, le <NSCoding> est suffisant)
Connectez-vous ou Inscrivez-vous pour répondre.