Sémaphores et P, V, Z, R
Chacha
Membre
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 :
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
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 />// Semaphore.h<br />// MozoDojo<br />//<br />// Created by Pierre Chatelier on 09/10/06.<br />// Copyright 2005, 2006, 2007 Pierre Chatelier. All rights reserved.<br />//<br /><br />#import <Cocoa/Cocoa.h><br /><br />#include <pthread.h><br /><br />@interface Semaphore : NSObject <NSCoding> {<br /> pthread_condattr_t cond_attr;<br /> pthread_cond_t cond;<br /> pthread_mutexattr_t mutex_attr;<br /> pthread_mutex_t mutex;<br /> volatile int* 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) R;<br />-(void) Z;<br /><br />//NSCoding protocol<br />-(id) initWithCoder:(NSCoder*)coder;<br />-(void) encodeWithCoder:(NSCoder*)coder;<br /><br />@end<br />
<br />//<br />// Semaphore.m<br />// MozoDojo<br />//<br />// Created by Pierre Chatelier on 09/10/06.<br />// Copyright 2005, 2006, 2007 Pierre Chatelier. All rights reserved.<br />//<br /><br />#import "Semaphore.h"<br /><br />@implementation Semaphore<br /><br />//designated initializer<br />-(id) initWithValue:(int)initialValue<br />{<br /> if (![super init])<br /> return nil;<br /> int error = pthread_condattr_init(&cond_attr);<br /> error = error ? error : pthread_cond_init(&cond, &cond_attr);<br /> error = error ? error : pthread_mutexattr_init(&mutex_attr);<br /> error = error ? error : pthread_mutex_init(&mutex, &mutex_attr);<br /> value = calloc(1, sizeof(int));<br /> error = error ? error : (!value);<br /> if (value)<br /> *value = initialValue;<br /> if (error)<br /> {<br /> [self autorelease];<br /> return nil;<br /> }<br /><br /> return self;<br />}<br />//end initWithValue:<br /><br />-(id) init<br />{<br /> return [self initWithValue:0];<br />}<br />//end init<br /><br />-(void) dealloc<br />{<br /> pthread_condattr_destroy(&cond_attr);<br /> pthread_cond_destroy(&cond);<br /> pthread_mutexattr_destroy(&mutex_attr);<br /> pthread_mutex_destroy(&mutex);<br /> free((void*)value);<br /> [super dealloc];<br />}<br />//end dealloc<br /><br />-(void) P:(int)n<br />{<br /> pthread_mutex_lock(&mutex);<br /> while(*value<n)<br /> pthread_cond_wait(&cond, &mutex);<br /> *value -= n;<br /> pthread_mutex_unlock(&mutex);<br /> pthread_cond_broadcast(&cond);<br />}<br />//end P:<br /><br />-(void) P<br />{<br /> [self P:1];<br />}<br />//end P<br /><br />-(void) V:(int)n<br />{<br /> pthread_mutex_lock(&mutex);<br /> *value += n;<br /> pthread_mutex_unlock(&mutex);<br /> pthread_cond_broadcast(&cond);<br />}<br />//end V:<br /><br />-(void) V<br />{<br /> [self V:1];<br />}<br />//end V<br /><br />-(int) R<br />{<br /> return *value;<br />}<br />//end R<br /><br />-(void) Z<br />{<br /> pthread_mutex_lock(&mutex);<br /> while(*value)<br /> pthread_cond_wait(&cond, &mutex);<br /> pthread_mutex_unlock(&mutex);<br />}<br />//end Z<br /><br />//NSCoding protocol<br />-(id) initWithCoder:(NSCoder*)coder<br />{<br /> return [self initWithValue:[coder decodeIntForKey:@"value"]];<br />}<br /><br />-(void) encodeWithCoder:(NSCoder*)coder<br />{<br /> [coder encodeInt:[self R] forKey:@"value"];<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
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Pour commencer :
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 :
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.
Ah, oui, exact, c'est une coquille. Merci
Tiens, je ne les connaissais pas, celle-là .
Oui, c'est juste pour éviter un warning, quand, comme moi, on aime à en activer un maximum.
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)