J'ai des données qui sont à la fois lues et mises à jour par plusieurs threads. Les lectures et les écritures doivent être atomiques. Je pensais à le faire comme ceci:Lecteur/graveur sans verrou
// Values must be read and updated atomically
struct SValues
{
double a;
double b;
double c;
double d;
};
class Test
{
public:
Test()
{
m_pValues = &m_values;
}
SValues* LockAndGet()
{
// Spin forver until we got ownership of the pointer
while (true)
{
SValues* pValues = (SValues*)::InterlockedExchange((long*)m_pValues, 0xffffffff);
if (pValues != (SValues*)0xffffffff)
{
return pValues;
}
}
}
void Unlock(SValues* pValues)
{
// Return the pointer so other threads can lock it
::InterlockedExchange((long*)m_pValues, (long)pValues);
}
private:
SValues* m_pValues;
SValues m_values;
};
void TestFunc()
{
Test test;
SValues* pValues = test.LockAndGet();
// Update or read values
test.Unlock(pValues);
}
Les données sont protégées en volant le pointeur pour chaque lecture et d'écriture, ce qui devrait threadsafe, mais il nécessite deux instructions enclenchées pour chaque accès. Il y aura beaucoup de lectures et d'écritures et je ne peux pas dire à l'avance s'il y aura plus de lectures ou plus d'écritures.
Cela peut-il être plus efficace que cela? Cela se verrouille également lors de la lecture, mais comme il est tout à fait possible d'avoir plus d'écritures puis de lectures, il n'y a pas lieu d'optimiser la lecture, à moins de ne pas infliger de pénalité à l'écriture. Je pensais à lire l'acquisition du pointeur sans une instruction verrouillée (avec un numéro de séquence), copier les données, puis avoir un moyen de dire si le numéro de séquence avait changé, auquel cas il devrait réessayer. Cela exigerait cependant des barrières de mémoire, et je ne sais pas si cela pourrait améliorer la vitesse.
----- ----- EDIT
Merci à tous, grands commentaires! Je n'ai pas réellement exécuté ce code, mais je vais essayer de comparer la méthode actuelle avec une section critique plus tard aujourd'hui (si j'ai le temps). Je suis toujours à la recherche d'une solution optimale, donc je reviendrai aux commentaires les plus avancés plus tard. Merci encore!
Quel est le problème avec l'aide des primitives de synchronisation de threads par défaut? – naivnomore
Je dois admettre que j'ai juste supposé que je pouvais le faire plus vite que ça. 1) Je ne montre qu'une seule instance ici, mais en réalité j'aurai peut-être 10000 instances de ces enregistrements de données protégées, ce qui signifierait 10000 sections critiques. Mais peut-être que ce n'est pas un problème, je ne sais pas, je n'ai jamais essayé quelque chose comme ça. 2) J'espérais pouvoir trouver quelque chose de plus rapide qu'une section critique. Il peut facilement y avoir des millions de lectures/écritures par seconde. Et sur le plan personnel, j'ai juste pensé que ce serait amusant de le faire aussi vite qu'humainement (machinely?) Possible. – Rabbit
Windows CRITICAL_SECTION est très léger à moins qu'il ne doive réellement bloquer. Je ne pense pas que des threads d'utilisateurs occupés comme celui-ci soient une très bonne idée - vous signalez implicitement au planificateur que vous avez beaucoup à faire, alors qu'en fait c'est le contraire. –