2017-11-18 6 views
0

Ich schreibe Code zum Laden und Parsen von Text für Daten in einem Spiel. Es gibt viele verzweigende Leser-Aufrufe - zum Beispiel, eine Karte wird Daten wie Requisiten und Feinde enthalten, und diese Objekte haben ihre eigenen Dateien, die aufgerufen und eingelesen werden. Dies wird normalerweise schnell geladen, etwa 1,5 Sekunden, aber auf langsameren Maschinen kann es dauern, 5+ Sekunden und das Spielfenster zu reagieren, bis es fertig ist.Async Data Reading/Parsing Leistung

Im Moment suche ich nach Möglichkeiten, das Fenster aktiv zu halten, während die Ladezeiten immer noch kurz sind. Ich habe einige der Ladevorgänge in Tasks getrennt, die im Hintergrund des Hauptthreads ausgeführt werden, und wenn das Laden beendet ist, teilt es dem Hauptthread mit, die Zustände zu wechseln, und das Spiel wird fortgesetzt. Dies funktioniert jedoch, nachdem ich eine zweite Ladezeit von einer zweiten Ladezeit auf eine zweite Ladezeit übergegangen bin. Ist das eine normale Leistung beim Wechsel zu einer Hintergrundaufgabe? Ich habe einen verallgemeinerten Code als Beispiel dafür angegeben, wie er derzeit gehandhabt wird.

Map map = null; 
//Main Update Loop 
public void Update() 
{ 
    if(GameState == Active) 
     map.Update(); 
    else 
     ShowLoadingScreen(); 
} 

//LoadWorld gets called from elsewhere, like a UI 
public async void LoadWorld() 
{ 
    GameState = State.Loading; 
    await Task.Run(() => { LoadFile("mapdata", out map); }); 
    GameState = State.Active; 
    map.Start(); 
} 

//This loads the file and reads the first line 
//which tells the reader what sort of object it is 
public void LoadFile(String file, out Map m) 
{ 
    m = new Map(); //create new map 
    StreamReader sr = new StreamReader(file); 
    String line; 
    Object obj = null; 
    while ((line = sr.ReadLine()) != null) 
    { 
     switch(line) 
     { 
     case "A": 
      obj = parseObjectA(line, sr); //continues with its own loop 
      break; 
     case "B": 
      obj = parseObjectB(line, sr); //continues with its own loop 
      break; 
     } 
     map.addObject(obj); 
    } 
} 

//This loops through the reader and fills an object with data, then returns it 
public Object parseObjectA(String line, StreamReader sr) 
{ 
    Object obj = new Object(); 
    while ((line = sr.ReadLine()) != null) 
    { 
     String element; 
     String value; 
     //parseLine is a function that breaks apart the line into an element and value 
     parseLine(line, out element, out value); 
     switch(element) 
     { 
     case "name": 
      obj.Name = value; 
      break; 
     case "position": 
      { 
       int pos = 0; 
       Int32.TryParse(value, out pos); 
       obj.position = pos; 
       break; 
      } 
     } 
    } 
    return obj; 
} 
+0

mir Sieht aus wie Sie die 'async' Methoden von Ihrem' StreamReader' im 'LoadFile' Methode verwenden könnte. Erwägen Sie auch, die Verwendung Ihres Stream-Readers in einem 'using'-Block zu verpacken. Ich lese weiter, um zu sehen, ob es noch etwas gibt, das die Leistung beeinträchtigen könnte. – Fabulous

+0

@Fabulous Von dem, was mir zuvor gesagt wurde, blockiert ReadLine keine Threads, da es sich um eine E/A-Operation handelt und ReadLineAsync könnte die Leistung sogar noch weiter verringern, da das Lesen nicht asynchron erfolgen muss Overhead. Ist das wahr? – DangerMcD

+0

Wenn Sie eine signifikante Verarbeitung der Daten durchführen, die Sie zurück erhalten, kann der Unterschied vernachlässigbar sein. Betrachten Sie [diesen Thread] (https://social.msdn.microsoft.com/Forums/vstudio/en-US/a47412ed-1b21-45c2-8554-cc602d6b5ddb/is-streamreaderreadlineasync-much-slower-than-readline?forum= Parallelextension), wenn dieses Problem angesprochen wird. Es gibt andere Aspekte der Frage, aber Ihre Frage wird angesprochen. Gewöhnlich "async/await" -Aufrufe kaskadieren den gesamten Call-Stack hinunter. In diesem Fall sollten Sie sich den Code in Ihrer 'parseLine'-Operation ansehen. – Fabulous

Antwort

0

Ich würde dies als Kommentar veröffentlichen, aber ich bin noch nicht erlaubt, so dass es in eine umfassendere Erklärung erweitert.

Aus einer Spielentwicklungsperspektive würde ich den Loader als Hintergrundarbeiter-Thread erstellen. Dadurch wird der Haupt-Thread/-Form des Fensters auf Benutzereingaben reagieren.

Innerhalb des backgroundWorker können Sie einzelne Aufgaben ausführen, um individuelle Workloads nach Bedarf auszuführen, aber die Benutzeroberfläche reagiert immer.

Wichtig ist, dass Sie die benötigten Variablen im Stammverzeichnis der Klasse erstellen und anschließend im Hintergrundthread auffüllen. Dies bedeutet, dass Sie dann die Ergebnisse leicht lesen können, sobald alles im Hauptthread abgeschlossen ist.

Als Beispiel:

class MainThread 
{ 
    BackgroundLoader bgLoader; 

    public MainThread() 
    { 
     bgLoader = new BackgroundLoader(); 
    } 

    public void LoadItems() 
    { 
     bgLoader.Load(); 
    } 
} 

class BackgroundLoader 
{ 
    BackgroundWorker backgroundWorker = new BackgroundWorker(); 
    // create the variables to hold the results. 
    Map map; 

    public void Load() 
    { 
     backgroundWorker.WorkerReportsProgress = true; 
     backgroundWorker.WorkerSupportsCancellation = true; 
     backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged; 
     backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted; 
     backgroundWorker.DoWork += BackgroundWorker_DoWork; 
     backgroundWorker.RunWorkerAsync(); 
     while (!backgroundWorker.IsBusy) { } 
    } 

    private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     // This is where you do the work and load your variables, which you then access once the thread is complete. 
     map = LoadMap(); 
    } 
} 
+0

Danke! Ich bin mir nicht sicher, ob das mein Problem löst, aber es lohnt sich, es genauer zu betrachten. – DangerMcD

+0

Da Sie bei der Ausführung von Aufgaben aus dem Hauptthread Verlangsamungen mit Tasks feststellen konnten, könnten Sie versuchen, den gesamten Ladestapel mit einem einzelnen Thread wie bisher auszuführen, aber vollständig in einem Hintergrundthread. Dies kann die Probleme beseitigen, die Sie sehen. – SoulRider

+0

Ich sehe! Danke für die Klarstellung :) – DangerMcD