Dovrei creare un sistema multi-thread che gestisca gli eventi di un gioco e li ordina, in modo indipendente, in diversi thread basati sulla priorità?

3

Posso creare un sistema multi-thread che gestisce gli eventi di un gioco e li ordina, indipendentemente, in diversi thread in base alla priorità, ed è una buona idea?

Ecco ulteriori informazioni:

Sto per iniziare a lavorare sul porting di un gioco di medie dimensioni da Flash / AS3 a Java, in modo da poter continuare lo sviluppo con funzionalità multi-threading.

Ecco alcuni dettagli sul gioco:

Il gioco contiene numerose attività asincrone, come "aggiornamento del mondo" (l'ambiente di gioco cambia continuamente in base a una serie di leggi e forze naturali), generazione procedurale di terreno, NPC, missioni, oggetti, ecc. e Inoltre, gli effetti di tutte le interazioni del giocatore con il suo ambiente sono calcolate in tempo reale, in base a un insieme di "statistiche" in continua evoluzione e, ancora una volta, leggi e forze naturali.

Tutte queste cose contemporaneamente, in modo asincrono, sembrano prestarsi molto bene al multi-threading.

La mia domanda è:

Posso creare una sorta di motore centrale che gestisce lo "stacking" di tutti questi eventi mentre vengono attivati e li ordina dinamicamente tra i thread disponibili e sarebbe una buona idea?

Ad esempio:

Essenzialmente, ogni volta che succede qualcosa (IE, un missile magico generato da un incantesimo, o un gruppo di piante ha bisogno di crescere fino allo stadio successivo), invece di elaborare proprio quell'attività proprio in quel momento e aggiungere il nuovo oggetto (s ) a un elenco di oggetti gestiti, invia un riferimento a tale evento a un "gestore di eventi" principale che lo getta in una pila di tutti gli altri eventi in coda, che poi li ordina e li ordina in base all'urgenza, li divide tra un numero di thread disponibili per l'esecuzione multithread più rapida possibile.

    
posta JonathonG 16.08.2012 - 16:13
fonte

5 risposte

8

Concorrenza Ahhh in Java :-). Giusto, quindi ho intenzione di dare qualche consiglio generale qui:

  1. Modella la tua elaborazione asincrona su una lavagna, poi fai in modo che qualcun altro entri in gioco e sfidi le tue ipotesi, specialmente l'ipotesi che ne hai bisogno in primo luogo (sembra che tu lo faccia, ma non mai fa male sfidarlo). Ci sono un sacco di strutture di dati concorrenti che puoi usare per modellare ciò che stai cercando di ottenere, vedi la mia menzione del libro di Brian per avere una buona guida pratica su queste cose (in Java).

  2. La concorrenza in Java è difficile - Gli oggetti sono mutabili per impostazione predefinita, il blocco implicito è difficile da correggere e spesso si ha a che fare con il costrutto di basso livello che è Thread stesso. Se hai intenzione di fare il lavoro in Java, leggi l'eccellente Concurrency Java Practice di Brian Goetz per assicurarti di evitare le trappole principali. Java 7 migliora ancora le cose, ma ci sono ancora alcuni problemi fondamentali che lo rendono difficile (per ora).

  3. Dato 2. Vale la pena guardare un framework o anche un'altra lingua sulla JVM che astrae da questo. Le scelte più popolari oggi includono il framework Akka (che supporta sia Scala che Java), GPars (Groovy) o out e out Clojure (Se ti piace Lisp).

HTH un po '!

    
risposta data 16.08.2012 - 16:41
fonte
4

All of these things going on at once, in an asynchronous manner, seem to lend themselves to multi-threading very well.

Per me sembra l'esatto opposto.

Il tuo "mondo" è un gigantesco gruppo di stati mutabili, che porta a tutti i tipi di incubi combinati con il multithreading.

La grande domanda è: cosa succede quando due eventi che vengono elaborati in parallelo influenzano la stessa parte del mondo in modi contraddittori e in che modo si impedisce al codice del processore degli eventi di vedere stati del mondo incoerenti? Ci sono molte varianti sottilmente diverse di questa domanda in cui ti imbatterai.

Forse alla fine si scoprirà che tali casi sono rari e che qualsiasi incongruenza risultante è tollerabile, ma in caso contrario causerà enormi mal di testa che potrebbero uccidere il progetto.

Ecco un interessante (anche se piuttosto breve) articolo sulle architetture di gioco multi-thread . E qui un altro articolo approfondito che menziona anche la sincronizzazione come un problema e motivo per cui molti giochi utilizzano un ciclo di gioco a thread singolo. Afferma che tali problemi possono essere evitati in gran parte facendo "multithreading non preventivo", ma non sono sicuro di come funzionerebbe esattamente.

    
risposta data 16.08.2012 - 16:55
fonte
2

Il multithreading basato su eventi ed eventi può funzionare molto bene - le tue idee hanno somiglianze con il mio attuale lavoro di multithreading in C ++ nativo. Inizi con i thread che ognuno ha i propri ruoli ben definiti da riprodurre nell'applicazione, che collaboreranno tra loro pubblicando attività ed eventi tra di loro da eseguire.

Ogni thread fa le sue cose e di volta in volta elabora i propri eventi e le attività che sono state accodate. Uno stato di blocco virtuale mette in attesa i thread mentre si bloccano. Ogni thread diventa un event processor e non avere nulla da fare diventa l'unico modo per bloccare in modo assoluto.

Ottenere il diritto multithreading è davvero complesso, ma diventa molto più complesso con i sistemi prioritari. La maggior parte degli eventi deve avvenire nell'ordine pubblicato comunque e le priorità possono riordinare le cose per causare la fame nei posti più inaspettati. Ricorda, devi essere in grado di eseguire il debug di questa roba. Io tengo solo le code normali e a bassa priorità e uso un flag a bassa priorità nell'attività per scegliere la coda al momento del post (evento). Sappi davvero che ti stai rendendo tangibile dal tuo sistema di priorità, prima di scegliere di usarlo.

Questo modello si adatta bene a qualsiasi numero di processori, perché è decentralizzato e consente a tutti i thread di postare ed elaborare eventi contemporaneamente. Il round trip di ping-pong di un task tra thread su diversi processori richiede meno di 500 cicli di macchina, rendendolo economico anche per attività brevi. L'esecuzione di attività ed eventi è veloce, con i suoi dati ora contenuti all'interno del thread di destinazione e pronti per essere pubblicati.

    
risposta data 16.10.2012 - 01:52
fonte
1

Certo, funzionerebbe. E in base ai requisiti del tuo ambiente, ha senso.

In sostanza, stai solo creando una versione più sofisticata del modello del pool di thread aggiungendo code prioritarie.

    
risposta data 16.08.2012 - 16:42
fonte
0

Whhyyyy? Non vedo la necessità che molti motori di gioco eseguano compiti asincroni diversi a destra ea sinistra, e molti di quelli commerciali no.

Di solito c'è un'elaborazione abbastanza omogenea per parallelizzare e ottenere un sacco di utilizzo della CPU senza avere tutti i tipi di cose non correlate che accadono simultaneamente, e in particolare le attività granulari come lanciare un incantesimo di missili magici. Inoltre, non riesco a vedere ciò che accade senza potenzialmente un sacco di sincronizzazione dei thread di un tipo che potrebbe sconfiggere tutti i guadagni prestazionali che speri di ottenere.

Suggerisco invece di pensare prima a dati / stato e cercare loop omogenei su un tipo di stato di gioco che nessun altro sistema acceda al momento in cui il sistema è in esecuzione.

Quindi è possibile eseguire quel sistema in parallelo, parallelizzando i loop o anche eseguendo sistemi in parallelo. Ad esempio, se solo i tuoi sistemi di movimento e rendering accedono ai componenti di movimento, esiste un ordine sequenziale in cui è possibile eseguire movimenti e quindi eseguire ripetutamente il rendering in un thread (poiché condividono lo stesso stato) ma altri thread che non si occupano di movimento tutto può essere eseguito contemporaneamente mentre quel thread si concentra sul movimento e sul rendering.

Inizia prima con lo stato e cerca di renderlo non più condiviso in più punti. Stato e dati prima, discussioni / attività secondarie. Disegna il tuo design multithreading intorno ai dati e come viene utilizzato al posto di un'idea più intuitiva di ciò che costituisce un'unità di lavoro. Pensa a qualcosa di simile a quale blocco di dati è possibile accedere in sicurezza da un thread in un dato momento.

E prova a trovare quei loop omogenei. Invece di un compito per un singolo incantesimo di missili magici, potresti avere un SpellSystem che scorre attraverso gli em> tutti che necessitano di elaborazione in un dato momento in un ciclo omogeneo. O forse è come un consumatore in cui usi una coda concorrente e spinge gli incantesimi per essere elaborati dal sistema di incantesimi, a quel punto si sveglia quando ci sono incantesimi da scartare ed elaborare mentre il resto del tuo sistema continua a fare qualsiasi altra cosa vuole fare. Ciò si presterà molto più facilmente all'elaborazione parallela efficiente. Cerca quei tipi di flussi di controllo in loop su dati che nessun altro sistema tocca o accede durante una buona quantità di tempo.

Prova ad associare un determinato tipo di dati a un dato sistema, come suoni per SoundSystem a riprodurre (o anche a livello inferiore, a quali regioni di memoria si accede in un dato momento da un determinato sistema). E naturalmente altri sistemi potrebbero aver bisogno di indicare direttamente o indirettamente i suoni per il sistema audio, ma possono farlo molto a buon mercato, a quel punto il SoundSystem fa il lavoro di sollevamento pesante. Potrebbe sembrare una sottile differenza, per esempio, solo arbitrariamente lanciare suoni asincroni svolgendo compiti a destra e sinistra, ma significa che hai messo un po 'in mente quel pensiero dicendo "Okay, questo tipo di dati viene gestito da questo tipo di sistema in questa specifica parte del codice e in questo thread specifico ", e questo può davvero aiutare a fare in modo che tu possa eseguire l'applicazione su molti thread in modo efficiente senza troppa discussione sui thread e, cosa ancora più importante, in sicurezza.

    
risposta data 19.12.2017 - 11:56
fonte

Leggi altre domande sui tag