Atomic est-il thread safe?

Hello,


Je reviens avec mon lot de question qui va faire de ce topic un topic sur 8 pages  xd


 


J'avais une question sur le nonatomic et plus précisément sur atomic.


 


Considérons la classe suivante (à  peu près):



@interface myClass ()
@property (atomic, retain) NSMutableArray *myArray;
@property (nonatomic) dispatch_queue_t queue;
@end

@implementation myClass

- (instancetype)init {
if ((self = [super init])) {
self.myArray = [NSMutableArray arrayWithCapacity:100];

// Create queue
self.queue = dispatch_queue_create("com.myQueue.request-queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_set_target_queue(self.queue, lowQueue);

}
return self;
}

Est ce que le fait de mettre myArray en atomic et de set une queue suffit à  "proteger" myArray?


 


Réponses

  • CéroceCéroce Membre, Modérateur

    Réponse rapide: utiliser atomic va placer un verrou (lock) au début des accesseurs et le retirer à  la fin.


    C'est ce qui rend l'opération atomique (= d'un seul tenant). Un autre thread devra donc attendre la fin de l'accès pour accéder à  la propriété.


     


    Cela dit, cela ne suffit pas à  rendre le programme thread safe: en effet, d'autres problèmes peuvent subvenir, notamment d'interblocage.


  • Ok, mais tous les accesseurs à  cette propriété sont fait dans cette même classe.


     


    En toute théorie, ça ne devrait pas poser de problèmes. Si?


  • AliGatorAliGator Membre, Modérateur
    Puisque myArray est atomique, comme l'a dit Céroce, la ligne "self.myArray = [NSMutableArray arrayWithCapacity:100]" va revenir à  l'équivalent d'un truc comme ça :

    NSLock* arrayLock = [NSLock new]; // Fait qu'une seule fois plus haut dans le code (ou via un dispatch_once)
    ...
    NSArray* array = [NSMutableArray arrayWithCapacity:100];
    [arrayLock lock];
    self.myArray = array;
    [arrayLock unlock];
    Cela protège la propriété myArray le temps de son accès (setter ou getter) mais c'est tout.

    Si par exemple plus tard tu itères sur cet array, le fait que le getter / la propriété soit atomique ne suffira pas pour autant à  rendre ton code thread-safe :

    for NSString* str in self.myArray {
    NSLog(@item: %@", str);
    }
    Par exemple ce code n'est pas thread-safe, car l'accès à  myArray est peut-être atomic quand tu exécutes self.myArray, mais rien ne garantit que pendant l'itération de ton tableau, il n'y ait pas un autre thread qui accède à  ton self.myArray lui aussi, et change son contenu, genre :
    [self.myArray removeAllObjects]
    Et dans ce cas si ce code removeAllObject qui s'exécuterait dans un autre thread s'exécute en plein pendant que ton thread 1 itère sur ton tableau de l'autre côté, badaboum...


    Donc mettre "atomic" garantit l'atomicité de l'accesseur (getter/setter) mais ça ne garantit pas que ton code soit thread-safe quand tu utilises la propriété pour autant, car garantir la thread-safety c'est quand même un peu + que juste mettre un "atomic".
  • samirsamir Membre


    Puisque myArray est atomique, comme l'a dit Céroce, la ligne "self.myArray = [NSMutableArray arrayWithCapacity:100]" va revenir à  l'équivalent d'un truc comme ça :



    NSLock* arrayLock = [NSLock new]; // Fait qu'une seule fois plus haut dans le code (ou via un dispatch_once)
    ...
    NSArray* array = [NSMutableArray arrayWithCapacity:100];
    [arrayLock lock];
    self.myArray = array;
    [arrayLock unlock];



     


     


    Plus exactement, l'atomicité est implémentée avec un "spin lock"


     


    https://github.com/opensource-apple/objc4/blob/master/runtime/Accessors.subproj/objc-accessors.mm#L104

  • AliGatorAliGator Membre, Modérateur
    Tout à  fait ce qui est logique vu les statistiques de risques de blocage pour un code aussi court qu'un setter/getter (mais bon je voulais expliquer avec du code-concept pour pas compliquer la chose ;-))
Connectez-vous ou Inscrivez-vous pour répondre.