异步数据读取/解析性能

时间:2017-11-18 21:52:41

标签: c# multithreading asynchronous io monogame

我正在编写代码来加载和解析游戏中数据的文本。有很多分支阅读器调用 - 例如,地图将包含道具和敌人等数据,并且这些对象有自己的文件被调用和读入。这通常都会快速加载,大约1.5秒< / strong>,但在较慢的机器上,它可能需要 5+秒并导致游戏窗口无响应,直到完成为止。

现在我正在寻找保持窗口活动同时保持加载时间短的方法。我已经将一些加载分离到在主线程后台运行的任务中,然后在加载完成时它告诉主线程切换状态并继续游戏。然而,这有效,我已经从 1.5 秒加载时间变为 53 秒加载时间。切换到这样的后台任务时,这是正常的性能吗?我发布了一些通用代码作为当前处理方式的示例。

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;
}

1 个答案:

答案 0 :(得分:0)

我会将此作为评论发布,但我尚未被允许,因此将其扩展为更全面的解释。

从游戏开发的角度来看,我会将加载器创建为后台工作线程。这将使窗口的主线程/形式响应用户输入。

在backgroundWorker中,您可以根据需要启动单个任务来执行单个工作负载,但UI始终是响应式的。

要记住的重要一点是在类的根中创建所需的变量,然后在后台线程中填充它们。这样做意味着您可以在主线程中完成所有内容后轻松读取结果。

举个例子:

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();
    }
}