La possibilità di una corsa di dati può essere ignorata in alcuni casi? (pur avendo thread concomitanti) [duplicato]

3

Sto programmando un motore di gioco come un progetto per hobby, e vorrei separare la fisica e disegnare in due thread separati. I vettori di posizione delle entità sono soggetti a modifiche dal thread di fisica, mentre il thread di disegno richiede solo l'accesso in lettura. In pseudocode:

Physics thread:
    loop
        ...
        read entity position
        compute new position for entity
        write entity position 
        ...
     end

Drawing thread:
    loop
        ...
        read entity position
        render entity
        ...
    end

La separazione del disegno e della fisica in due thread consente il disaccoppiamento della frequenza di aggiornamento della fisica dal framerate del disegno. Questo permette cose come effetti di movimento facile lenti e frame rate variabile, ma rende anche possibile per il filo fisica per modificare un vettore posizione al tempo stesso esatto come filo disegno sta leggendo lo stesso valore.

Spero di poter andare via senza usare mutex, lock o altri controlli di concorrenza per i vettori di posizione. La validità dei dati di posizione non è critica e sono tollerabili piccoli errori derivanti dall'accesso non atomico. La mia esperienza con la programmazione concorrente è tuttavia abbastanza limitata e temo che ci siano altri problemi derivanti dall'accesso asincrono di cui non sono a conoscenza. Quali sono gli svantaggi di questo approccio? Quali alternative ci sono?

Sto programmando in C ++ 11 (usando std :: thread), su un PC x86 multi-core.

    
posta jms 06.05.2014 - 09:22
fonte

1 risposta

3

L'architettura suggerita presenta una corsa di dati. Se questo è trascurabile dipende dalle specifiche del gioco e dai dettagli dell'implementazione. Poiché il thread di rendering può accedere allo stato condiviso in qualsiasi momento, questo stato deve sempre trovarsi in uno stato coerente e valido.

Ad esempio, consideriamo cosa potrebbe accadere se il thread fisico sposta un oggetto. Se prima aggiorna la coordinata x , quindi y e z , quindi tra o durante tali aggiornamenti il thread di rendering può accedere alla posizione. Se il nostro oggetto viene spostato da (0, 0, 0) a (1, 1, 1) , il thread di rendering potrebbe ad esempio visualizzare il valore intermedio (1, 0, 0) . Questo non è assolutamente un problema se per un frame l'oggetto appare due pixel a sinistra. Ma se il tuo gioco presenta oggetti che si muovono velocemente o si teletrasporta sulla mappa, il risultato potrebbe essere più evidente.

Come si può evitare? I dati devono essere aggiornati atomicamente per fornire una vista coerente sullo stato. Ad esempio:

  • L'intero stato è protetto da un mutex. Il thread di fisica ha accesso in scrittura o il thread di disegno ha accesso in lettura. Poiché il thread di fisica ha sempre accesso in lettura, può continuare a elaborare e mettere in coda una serie di modifiche da applicare una volta recuperato l'accesso. Questa è una soluzione subottimale in quanto il filo del disegno può essere bloccato per un lungo periodo di tempo (rispetto al frame rate desiderato).

  • Lo stato di ciascun oggetto di gioco è protetto da un mutex. Ciò rende molto meno probabile che i due thread richiedano l'accesso allo stesso oggetto allo stesso tempo, ma ora dobbiamo ottenere molti più blocchi che degraderanno anche le prestazioni. Questa soluzione può ancora presentare incoerenze minori, come due oggetti che sembrano intersecarsi sullo schermo, ma in realtà sono solo molto vicini tra loro, poiché al momento del disegno solo un oggetto è stato aggiornato.

  • Lo stato del gioco esiste esclusivamente di puntatori a vari oggetti di gioco. Quando un oggetto deve essere aggiornato, prima viene creata una copia di quell'oggetto. Quindi, le modifiche vengono applicate alla copia. Una volta che l'oggetto si trova in uno stato coerente, il puntatore a quell'oggetto viene aggiornato in modo atomico per puntare alla copia mutata. Questa soluzione può ancora presentare incoerenze minori, come con la soluzione precedente. Questo potrebbe essere implementato in termini di blocchi, ma in realtà è richiesto solo un aggiornamento atomico.

risposta data 06.05.2014 - 10:12
fonte

Leggi altre domande sui tag