J'ai un grand tableau (3e9 éléments) de données, et je mets à jour sa valeur dans plusieurs threads. Je viens de découvrir qu'il y a des conditions de course.Puis-je utiliser une partie de mes données comme verrou?
Je pense qu'il est inutile de verrouiller toute la fonction, comme les éléments sont indépendants les uns des autres, la mise à jour sur et data[234]
peut être effectuée en toute sécurité en même temps.
J'ai également trouvé le bit le plus significatif de chaque élément dans data[]
ne sera jamais utilisé. Est-il sûr d'implémenter un verrou intégré atomique GCC sur ce bit?
Mon code est le suivant, mais il semble qu'il se bloque.
const unsigned short LOCK_MASK = 1<<15;
unsigned short * lock = &(data[position]);
unsigned short oldLock, newLock;
//lock
do {
oldLock = *lock;
newLock = oldLock^LOCK_MASK;
} while ((oldLock & LOCK_MASK) || !__sync_bool_compare_and_swap(lock, oldLock, newLock));
//update data[position] here
...
...
...
//unlock
*lock ^= LOCK_MASK;
J'ai aussi lu ce post (Lightweight spinlocks built from GCC atomic operations?) et a ajouté volatile
sur mon data
EDIT Dans ma conception, 0 signifie déverrouillé et 1 signifie verrouillé
Toutes vos lectures avant l'acquisition de la serrure doivent être synchronisées. En particulier, 'oldLock = * lock;' est faux, cela doit être atomique. En l'état, l'optimiseur peut supposer que '* lock' ne change jamais si' (oldLock & LOCK_MASK) 'était vrai une fois. –
Une approche plus prudente pourrait consister à allouer un nombre approprié de mutex réels (ou spinlocks ou tout mécanisme que vous souhaitez utiliser) et à les utiliser pour synchroniser l'accès à vos données. par exemple. si vous avez un tableau de M mutex, alors chaque thread verrouille mutex # (i% M) avant de lire ou d'écrire la valeur #i dans le tableau de données. Réglez la valeur de M jusqu'à ce que vous trouviez la plus petite valeur pour laquelle la contention pour les mutex est encore assez rare. –