2010-03-13 5 views
3

J'ai développé un jeu basé sur AJAX où il y a un bug causé (très distant, mais en volume au moins une fois par heure) où deux requêtes sont envoyées presque simultanément à la page de traitement (la dernière que j'ai suivie , les demandes étaient une différence de .0001 ms). Il y a une vérification juste avant que la requête soit exécutée pour s'assurer qu'elle n'est pas exécutée deux fois, mais comme la différence est si petite, la vérification n'est pas terminée avant que la requête suivante ne soit exécutée. Je suis perplexe, comment puis-je empêcher cela car il provoque de graves problèmes dans le jeu. Pour être plus clair, la requête commence une nouvelle partie dans le jeu, donc quand elle exécute deux fois, elle commence 2 tours en même temps ce qui casse le jeu, donc je dois pouvoir arrêter le script d'exécuter si le tour précédent n'est pas terminé, même si le tour précédent a commencé il y a .0001 ms.Arrêter 2 requêtes identiques d'exécuter presque simultanément?

Répondre

3

L'astuce consiste à utiliser une instruction de mise à jour atomique pour faire quelque chose qui ne peut pas réussir pour les deux threads. Ils ne doivent pas réussir à la fois, de sorte que vous pouvez simplement faire une requête comme:

UPDATE gameinfo SET round=round+1 WHERE round=1234 

Où 1234 est le cycle actuel qui était en cours. Ensuite, vérifiez le nombre de lignes affectées. Si le thread affecte zéro rangs, il a échoué et quelqu'un d'autre l'a fait auparavant. Je suppose que ce qui précède sera exécuté dans sa propre transaction car la validation automatique est activée.

1

Tout ce dont vous avez vraiment besoin, c'est d'un mutex large. flock() sem_acquire() fournit ceci - mais seulement au niveau du système - si l'application est répartie sur plusieurs serveurs, vous devez utiliser memcached ou implémenter votre propre serveur socket pour coordonner les nœuds.

Vous pouvez également utiliser une base de données comme zone de stockage commune (par ex. avec MySQL acquérir un verrou sur une table, vérifier quand le tour a été démarré, si nécessaire mettre à jour la rangée pour dire qu'un nouveau round commence (et se souvenir de cela - puis débloquer la table ...

C .

0

écluses sont une façon de faire cela, mais d'une manière plus simple dans certains cas, est de rendre votre idempotent de demande. cela signifie simplement que l'appelant a à plusieurs reprises exactement le même effet que l'appelant une fois.

Par exemple , votre appel en ce moment le fait effectivement $round++; Les appels clairement répétés à ceci causeront le problème

Mais vous pouvez plutôt faire $round=$newround; Ici, les appels répétés n'auront aucun effet, car le tour a déjà été défini et le second appel le définit simplement sur la même valeur.

Questions connexes