2011-01-02 14 views
7

Ho implementato un ciclo motore di gioco come segue:Esistono le migliori pratiche per l'implementazione di un ciclo asincrono del motore di gioco?

public static Boolean Start () 
{ 
    if (hasBoard) 
    { 
     // start engine on worker thread 
     asyncTask = new AsyncResult (stopEngine, asyncTask); 
     isRunning = ThreadPool.QueueUserWorkItem (startEngine, asyncTask); 

     if (isRunning) 
     { 
      Console.WriteLine ("[{0}] Engine started", 
       DateTime.Now.ToString ("hh:mm:ss")); 
     } 
     else 
     { 
      Console.WriteLine ("[{0}] Engine failed to start", 
       DateTime.Now.ToString ("hh:mm:ss")); 
     } 
    } 

    return isRunning; 
} 

public static void Stop () 
{ 
    Console.WriteLine ("[{0}] Engine stopping", 
     DateTime.Now.ToString ("hh:mm:ss")); 

    asyncTask.SetAsCompleted (null, false); 
} 

private static void startEngine (Object task) 
{ 
    while (!((IAsyncResult)task).IsCompleted) 
    { 
     Thread.Sleep (10000); 

     Console.WriteLine ("[{0}] Engine running", 
      DateTime.Now.ToString ("hh:mm:ss")); 
    } 
} 

private static void stopEngine (IAsyncResult iaResult) 
{ 
    // clean up resources 

    Console.WriteLine ("[{0}] Engine stopped", 
     DateTime.Now.ToString ("hh:mm:ss")); 

    isRunning = false; 
} 

Sto usando la classe AsyncResult consigliato da Jeff Richter nel suo articolo, Implementing the CLR Asynchronous Programming Model. Per essere in grado di fermare il motore dall'interfaccia utente, l'implementazione che ho utilizzato era un po 'diversa dallo schema asincrono standard. Questa implementazione funziona come previsto, ma quando mi allontano da una pratica standard, torno alla comunità SO per garantire che sto facendo le cose nel modo giusto.

Ci sono problemi con questa implementazione che chiunque può vedere?

+0

in un ciclo di gioco, non si genera semplicemente una nuova discussione anziché utilizzare un thread di pool? –

+0

@Mitch: non ne sono proprio sicuro. Credo che la generazione di una nuova discussione sia standard. Non so che ci sia qualcosa di sbagliato nell'usare un thread di Threadpool. Questo motore di gioco è super leggero. Inoltre, dal momento che l'interfaccia utente di WinForms è * in controllo *, ho pensato che sarebbe stato un buon modo per gestire il ciclo di gioco. – IAbstract

+1

Perché stai usando * qualsiasi * thread in un ciclo di gioco? Se * hai * bisogno di qualsiasi (che dovrebbe essere davvero messo in discussione), i thread dovrebbero probabilmente essere thread pre-spawnati per uno * scopo ben definito * (ad es. Sempre 2 thread, ecc. Che vivono continuamente) che funzionano fuori da una coda o altra sincronizzazione. Tuttavia, il migliore è probabilmente solo per tenerlo "KISS". –

risposta

11

Dato che suona come un progetto su cui hai il controllo, ti suggerirei di abbandonare APM e utilizzare il modello basato su Task fornito in .NET4. Questo è l'approccio consigliato per .NET4 anziché APM. La classe Task fa parte della Libreria parallela attività (TPL) ma è ottima anche per questi lavori asincroni di base.

private CancellationTokenSource cts; 

public void StartEngine() 
{ 
    if (cts == null) 
    { 
     cts = new CancellationTokenSource(); 
     Task.Factory.StartNew(() => GameLoop(cts.Token), cts.Token); 
    } 
} 

private void GameLoop(CancellationToken token) 
{ 
    while (true) 
    { 
     token.ThrowIfCancellationRequested(); 
     Thread.Sleep(1000); 
     Debug.WriteLine("working..."); 
    } 
} 

public void StopEngine() 
{ 
    if (cts != null) 
    { 
     cts.Cancel(); 
     cts = null; 
    } 
} 
+2

Concordato, ma aggiungerei l'opzione LongRunning durante la creazione dell'attività. –

+0

Ho apportato la modifica per aggiungere l'opzione LongRunning: 'Task.Factory.StartNew (() => startEngine (cancelSource.Token), cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default)' – IAbstract

2

Direi che è implicito che le attività sono di breve durata quando si utilizza il pool di thread poiché il numero di thread in esso è limitato.

Nel tuo caso utilizzerei un BackgroundWorker poiché hai menzionato le winform. BW gestisce la sincronizzazione per te e quindi è in grado di aggiornare la GUI senza utilizzare InvokeRequired ecc.

+0

* È implicito che le attività sono di breve durata quando si utilizza il pool di thread * - non secondo Jeff Richter ... a meno che non mi sia sfuggito qualcosa. Tuttavia, vedo il tuo punto nel mantenere le attività threadpool thread-breve. – IAbstract

+0

@IAbstract: hai un riferimento per questo? Sarebbe interessante da leggere. – jgauffin

+0

Dovrei trovarlo di nuovo, è passato più di un anno e mezzo da quando ho fatto il commento. L'implicazione che un'attività è di breve durata potrebbe essere dovuta al trattamento di default - cioè, dobbiamo impostare un'attività come di lunga durata se ci aspettiamo tempi di esecuzione lunghi. Deve esserci stato qualcosa di significativo in ciò che ho commentato, ma cercherò di trovare ciò a cui mi riferisco. ;) – IAbstract

Problemi correlati