【发布时间】:2017-11-18 21:52:40
【问题描述】:
我正在编写代码来加载和解析游戏中的数据文本。有很多分支读取器调用 - 例如,地图将包含道具和敌人等数据,并且这些对象有自己的文件被调用和读取。这通常会很快加载,大约 1.5 秒,但在速度较慢的机器上,它可能需要大约 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;
}
【问题讨论】:
-
在我看来,您可以在
LoadFile方法中使用StreamReader中的async方法。还可以考虑将流阅读器的使用情况包装在using块中。我会继续阅读,看看是否还有其他可能影响性能的因素。 -
@Fabulous 根据我之前的说法,ReadLine 不会阻塞线程,因为它是一个 I/O 操作,而且 ReadLineAsync 可能会进一步降低性能,因为实际读取不需要异步完成,因此使用 ReadLineAsync 会增加不必要的开销。这些是真的吗?
-
如果您正在对返回的数据进行一些重要的处理,则差异可能可以忽略不计。考虑解决该问题的this thread。那里的问题还有其他方面,但您的问题已得到解决。通常
async/await调用一直沿调用堆栈向下级联。在这种情况下,您可能需要查看parseLine操作中的代码。 -
@Fabulous parseLine 只是将字符串拆分为我可以在开关中使用的值。因此,如果该行看起来像“name”“Joe”,我将返回 'name' 作为元素,并返回 'Joe' 作为值。我认为在代码示例中添加并不重要。
-
加载文件时通常处理多少行?是否可以并行处理这些行?如果一条线没有直接受到另一条线的影响,您可能需要调查一下。如果没有运行代码的能力,就不可能查明确切的原因。您可能会从并行化中受益。
标签: c# multithreading asynchronous io monogame