2010-05-16 15 views
2

J'ai 2 threads dans mon application, un thread de mise à jour de jeu et de rendu/IO/thread principal. Mon thread de mise à jour met à jour l'état du jeu et le thread de rendu restitue la scène en fonction des valeurs mises à jour des modèles d'état du jeu et de quelques autres variables stockées dans un objet (gameEngine).Qu'est-ce qui ne va pas avec mon bloc @ synchronisé?

Le rendu fil est exécuté alors que le fil de jeu est mise à jour encore, ce qui est un problème, il me semble que la solution est d'utiliser @synchronized comme ceci:

 @synchronized(gameEngine) 
     { 
      [gameEngine update]; 

      nextUpdate = now + GAME_UPDATE_INTERVAL; 

      gameEngine.lastGameUpdateInterval = now - lastUpdate; 
      gameEngine.lastGameUpdateTime = now; 
      lastUpdate = now; 
     } 

Mais le fil rend les accès encore l'objet gameEngine entre -update et les 3 dernières lignes du bloc. Pourquoi est-ce?

+0

Veuillez également indiquer le code que vous prévoyez d'exécuter sur le thread de rendu. Est-il également à l'intérieur d'un bloc @synchronize sur l'objet gameEngine identique? – Ken

Répondre

10

@synchronized n'empêche pas les autres threads d'accéder à gameEngine. Il bloque simplement les autres @synchronized avec le même objet. Cela signifie que dans

// thread A: 
@synchronized(a) { 
    do_A(a); 
} 
... 
// thread B: 
do_B(a); 

do_A et do_B peut se produire ensemble, mais dans

// thread A: 
@synchronized(a) { 
    do_A(a); 
} 
... 
// thread B: 
@syncrhonized(a) { 
    do_B(a); 
} 

do_A et do_B sera toujours exécuté de manière séquentielle.

+0

C'est exactement ce que je faisais et ce que je devais faire pour le réparer. – Morrowless

+0

C'est une excellente réponse qui m'a beaucoup aidé, merci. – krafter

0

Vous ne devriez pas verrouiller le jeu. - @KennyTM a expliqué ce que je crois être la bonne réponse à votre question, mais l'implémenter ne ferait qu'exécuter le moteur du jeu ou le moteur de rendu à un moment donné , ce qui en fait essentiellement un seul thread. Ce que vous devez faire est d'utiliser un objet d'état immuable, qui peut être un Ivar de moteurJeu, mais devrait être nonatomic, où la fonction render vous prenez l'état comme si

State *state = [[gameEngine state] retain]; 

puis utilisez l'état, le relâcher une fois terminé. Lorsque gameEngine effectue une mise à jour, il ne doit pas muter les données dans son état ivar, qui pourrait être utilisé par le moteur de rendu, mais peut en faire une copie. Pour définir l'état, il devrait

State *oldState = state; 
state = newState; //newState should have a retainCount of 1 
[oldState release]; 

parce que si vous relâchez oldState avant de l'état à newState alors le moteur de rendu pourrait obtenir oldState, qui vient d'être désaffectée, conduisant à de mauvaises choses.

+0

-1, vous avez une condition de concurrence qui pourrait amener le moteur de rendu à faire référence à un objet désalloué. '[[gameEngine state] conserve]' n'est pas atomique. Si le rendu est interrompu après le retour de 'gameEngine state' mais avant d'envoyer retain et que le moteur de jeu est autorisé à mettre à jour complètement l'état incluant' [oldState release] 'avant que le rendu ne soit planifié, l'objet pourrait être désalloué. – JeremyP

+0

Le moyen le plus sûr d'atteindre ce que vous voulez est de faire de '-state' une propriété atomique. Cela conduit à une petite section de code synchronisé, mais la partie à un seul thread est effectivement limitée au temps nécessaire pour envoyer et conserver automatiquement l'ivar. – JeremyP

+0

même si l'état était atomique, cela signifie simplement que - [gameEngine state] retournerait une référence valide, mais pourrait disparaître avant que l'appel à retain ne soit appelé. Cela ne veut pas dire que j'ai raison - loin de là, je vous remercie d'avoir invoqué ma condition de course, mais à cause de la façon dont l'état est défini, - [gameEngine] est toujours sûr d'avoir au moins un compte. –