9
votes

Variables statiques sécurisées

Y a-t-il un moyen d'atteindre l'objectif C que je peux définir un INT statique qui est le fil sûr?

Par exemple si j'ai la classe appelée session qui a: xxx

Je construisons des objets de session à partir de différents threads, chaque objet de session devrait avoir une pièce d'identité unique.


4 commentaires

Vous devez utiliser une sorte de sémantique de verrouillage. Le problème est que vous souhaitez que l'opération soit atomique. Mais gardez à l'esprit que "++" n'est pas atomique du tout, c'est quelques opérations à la fois.


est possible alors d'entourer mon retour session_id ++ avec synchronisé (auto) {retour session_id ++}. Cependant, je ne suis pas sûr que cela verrouille la classe ou l'objet lui-même.


Comme écrit, il verrait l'instance, pas la classe (pas ce que vous voulez). Peut-être " @synchronisé ([auto-classe]) "?


@David Gelhar: Non, ne vous synchronisez pas sur [auto classe] car les instances de sous-classes seraient sychronisées sur différents objets de classe. Synchronisez sur une classe explicite par exemple. [Classe MyClass]


4 Réponses :


7
votes

Si vous parlez de cacao, la fonctionnalité mutex est fournie par nslock et nsrecursivockock .

Pour protéger correctement la ressource non atomique, vous avez besoin Ces mutiles, de peur de multiples threads peuvent essayer de modifier les données en même temps (conduisant à une corruption) ou d'utiliser les données dans un état à moitié modifié (conduisant à des données non valides).

Votre code regarderait quelque chose comme ceci: xxx

Si vous n'utilisez pas de cacao (ou quelle petite programmation de cacao, je me souviens de mon brève interlude avec un IMAC est si faiblement rappelé que c'est presque inutile), Il suffit d'utiliser le concept, de la traduire comme une langue ou un cadre que vous avez:

  • Verrouillez le mutex avant d'utiliser ou de changer une ressource protégée.
  • Utilisez ou changez la ressource.
  • Déverrouillez le mutex.
  • Bonus Conseil 1: Verrouillez le mutex aussi tard que possible et déverrouillez-le dès que possible.
  • Bonus Conseil 2: Seulement verrouiller ce dont vous avez besoin pour éviter les retards inutiles.

    Expliquant ce dernier point un peu plus: si vous synchronisez sur auto pour deux choses totalement non liées (disons un identifiant de session et un identifiant utilisateur), ils se bloqueront malgré le fait que ce n'est pas nécessaire de le faire. Je préférerais que deux mutiles distincts maintiennent la granularité faible.

    Bien sûr, si vous n'avez que un mutex sur l'ID de session seule (mais voir ci-dessous pour la mise en garde), n'hésitez pas à utiliser synchronisé ( auto) mais je préférerais le faire mon chemin, donc je ne me ferais pas surpris en ajoutant une autre ressource protégée plus tard.

    Dans tous les cas (c'est la mise en garde mentionnée), vous allez Trouvez probablement que la synchronisation sur auto ne protégerait pas de manière adéquate une variable statique, qui serait partagée sur plusieurs objets. Le mutex devrait appartenir aux données plutôt que tout ce qui l'utilise.


9 commentaires

Je viens de Java World, où vous pouvez simplement faire: statique synchronisée INT générateid () {return sessionId ++}. Est-ce que cela sera compatible si je le fais - (int) Généralités {synchronisé (auto) {retour session_id ++;}}?


Je penserais que cela fonctionnerait bien bien qu'un verrouillage de soi (l'objet) est probablement plus large que moi. Au lieu de cela, je préférerais un mutex spécifique afin d'éviter les retards de mutex inutiles (voir mon conseil supplémentaire de bonus).


@ user489579 Fermer, mais vous ne voulez pas synchroniser sur auto pour une méthode d'instance qui accède à une variable statique; peut-être synchroniser sur [auto-classe] ou sur un objet directement associé à la valeur que vous modifiez (par exemple, enveloppez l'int dans un nombre NSNumber et Sync sur le NSNUMBER).


Ah, bon point, j'ai oublié qu'il était statique alors pourrait être utilisé par plusieurs objets , je vais ajuster la réponse.


David, je vois votre point et j'ai pensé à envelopper ou à déclarer la norme NSN au lieu d'INT, mais où / lorsque vous allez libérer exactement le nombre de NSN au fur et à mesure que cela sera (peut être) accessible en construisant d'autres objets pour générer leurs identifiants.


Je ne penserais pas que vous auriez besoin de vous inquiéter de la libérer. Vous pouvez le créer et l'initialiser à 0 dans une méthode d'initialisation + (voir Stackoverflow.com/questions/992070/... ); Mais une fois créé, ça ne va pas disparaître.


Pensez à utiliser la Dispatch_Semaphore_Create de GCD au lieu de Nslock. Ceux-ci peuvent être verrouillés et déverrouillés à partir de n'importe quel thread, contrairement à Nslock qui doit toujours être utilisé sur le même thread.


@Davidgelhar Selon @jeremyP dans un commentaire sur la question, vous devez synchroniser sur [Classe MyClass] , pas [Classe auto] . Il semble que @jeremyP dit [auto classe] retournera un objet différent à chaque fois, pas un singleton.


@Liron Ce que Jeremype dit n'est pas que [auto-classe] renvoie un objet différent à chaque fois. Il disant que si vous avez une sous-classe mysubclass qui hérite de myClass, [auto classe] retournera [mysubclass class] quand appelé à partir d'une instance de mysubclass . C'est un point valide.



9
votes

Je pense que vous ferez mieux d'utiliser Opérations atomiques pour modifier session_id . Un Question précédente Discussions sur les opérations d'incrément atomique / décrémentation pour OS X et < Un href = "http://www.cocoadev.com/index.ploodev.com/index.pl?osatomic" rel = "nofollow NOREFERRER"> Cette page parle des Osatomic fichier d'en-tête. Opérations atomiques sur les entiers, ce qui est facilement soutenu au piètre-matériel, sera probablement sensiblement plus rapide que d'utiliser des constructions de verrouillage.


2 commentaires

D'accord. Si tout ce que vous faites, c'est rester autour d'une statique aux fins d'une valeur croissante d'incrémentation, utilisez osatomicincrip32 , qui sera sûr et ne disposera de toutes les frais généraux des primitives de synchronisation de cacao ou de posix.


développeur.apple.com/library/mac/#documentation/cocoa/concept UAL / ...



1
votes

Il existe de nombreuses options, y compris (de haut niveau à bas niveau), la directive @Synchronized objectif-c, nslock , pthread_mutex_lock et des opérations atomiques.

Lire la section "Synchronisation" du Guide de programmation de filetage pour plus de détails.


0 commentaires

2
votes

Une réponse 4 ans plus tard actuellement dans iOS8. : o) Le meilleur pour moi est d'utiliser une classe Singleton comme suit:

(votrefile.h) p> xxx pré>

===================== ======================================== (YOURFILE.M) P>

#import "singletonSessionId.h"

@implementation singletonSessionId
@synthesize value = _value;

+ (singletonMsgNbr*)sharedSingleton {

    static singletonSessionId *instance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[singletonSessionId alloc]init];
        instance.value = 1000;
    });

    return instance;
}

- (void)generateSessionId {
    _value += 1;
}

@end


6 commentaires

@ondermerol: En effet, et c'est exactement ce que cela est censé faire => "Il vous suffit d'appeler la méthode" Généralités "pour chaque nouvelle valeur d'identification".


Désolé pour la réponse tardive, cela fonctionne comme un charme, je n'ai commis une erreur que.


@ Xle_22: Ce fil est-il sûr? Supposons que THIP1 appelle des appels de généraux, puis thready2 appels générés à Thread1 a eu une chance de récupérer la valeur? Ils n'utilisaient-ils pas tous les deux le même identifiant? Et s'ils appellent tous les deux la méthode en même temps, il n'y a-t-il pas de potentiel de corruption de mémoire car aucun outil de synchronisation n'est utilisé?


@ jk7 DiscuTatch_once bloquera tous les appels jusqu'à ce que l'un d'entre eux réussisse. Les documents disent "si appelé simultanément de plusieurs threads, cette fonction attend de manière synchrone jusqu'à ce que le bloc soit terminé." En fait, la méthode a été spécifiquement conçue pour ce cas d'utilisation: "Cette fonction est utile pour l'initialisation des données globales (singletons) dans une application. Appelez toujours cette fonction avant d'utiliser ou de tester toutes les variables initialisées par le bloc."


Ce n'est pas le fil-danger pour exactement la raison pour laquelle @ JK7 stipule. Le Dispatch_Oronce n'a rien à voir avec cela, car il n'est pas appelé lors de l'appel des générations


@ JK7: 'Fishinear' a raison. À ce moment-là, j'ai pensé avoir un fil parfait Safe Singleton était suffisant pour générer et incrémenter une variable de manière sûre. Ce n'est certainement pas le cas. À mon avis, la meilleure façon de lire et d'écrire une variable d'instance sans condition de course n'est d'utiliser la fonction «Dispatch_barriers» fournie par Grand Central Dispatch. Peu de choses à mettre en œuvre mais un résultat sera sûr à 100%.