kUr*_*4m4 5 c# io multithreading unity-game-engine
我想在我的应用程序中做一些耗时但对实际游戏没有影响的东西.因此,我试图使用一个线程分别运行这些东西.以下是我创建线程的方法:
Thread thread = new Thread(() => WriteCheckpointTextFile (myFileName, checkpoint));
thread.Start();
Run Code Online (Sandbox Code Playgroud)
这是我在WriteCheckpointTextFile中运行的代码:
void WriteCheckpointTextFile (string fileName, Checkpoint checkpoint)
{
string checkpointToText = "";
checkpointToText += CheckpointDataNames.nextMarker + Environment.NewLine;
checkpointToText += checkpoint.nextMarker + Environment.NewLine;
checkpointToText += "%" + Environment.NewLine;
checkpointToText += CheckpointDataNames.difficulty + Environment.NewLine;
checkpointToText += checkpoint.difficulty.ToString () + Environment.NewLine;
checkpointToText += "%" + Environment.NewLine;
checkpointToText += CheckpointDataNames.musicObject + Environment.NewLine;
checkpointToText += checkpoint.musicObject + Environment.NewLine;
checkpointToText += "%" + Environment.NewLine;
checkpointToText += CheckpointDataNames.gameTimePassed + Environment.NewLine;
checkpointToText += checkpoint.gameTimePassed.ToString () + Environment.NewLine;
checkpointToText += "%" + Environment.NewLine;
checkpointToText += CheckpointDataNames.storedStats + Environment.NewLine;
checkpointToText += EndRaceDataString (checkpoint.storedStats);
checkpointToText += "%" + Environment.NewLine;
checkpointToText += CheckpointDataNames.pursuers + Environment.NewLine;
foreach (PursuerData p in checkpoint.pursuers) {
checkpointToText += p.pursuerName + "," + p.pursuerNextMarker + "," + p.pursuerPosition.x.ToString () + "," + p.pursuerPosition.y.ToString () + "," + p.pursuerPosition.z.ToString () + Environment.NewLine;
}
checkpointToText += "%" + Environment.NewLine;
#if !UNITY_WEBPLAYER
System.IO.File.WriteAllText (fileName, checkpointToText);
#endif
Debug.Log("Finished writing text to file");
}
Run Code Online (Sandbox Code Playgroud)
现在出于某种原因,上次调试并不总是打印出来,并且我的文件并不总是被写入.可能是什么导致了这个?
问题是,当checkpoint.storedStats您在后台线程中迭代集合时,另一个线程(可能是主线程)正在修改您的集合。这会抛出一个InvalidOperationException. 由于您的代码在后台线程上执行,因此 Unity 播放器/引擎会默默地抛出、未处理和忽略异常。
不幸的是,这意味着您必须同步或限制对checkpoint.storedStats集合的访问和更改,以便只有 1 个线程可以对其进行修改,并且在执行此操作时,其他线程不得对其进行迭代。
很难评论您实现的最佳或最简单的方法,因为我不知道您如何storedStats在整个应用程序中使用该集合。一种可能且相当典型的解决方案是用一条语句包装所有访问集合的lock位置。这将确保在任何给定时间只有 1 个线程进入锁定块。然后,当访问storedStats用于读取(而不是写入)时,您可以在游戏对象的脚本上向集合提供一个方法lock,制作它的副本,然后返回该副本。由于它是副本,因此您可以安全地对其进行迭代:
public class Checkpoint : MonoBehaviour
{
//used for locking
private readonly object ThreadLocker = new Object();
private List<Stat> storedStats = new List<Stat>();
public List<State> GetStoredStatsCopy()
{
lock(ThreadLocker)
{
//makes a copy/snapshot of the list
return new List<Stat>(storedStats);
}
}
//sample code that changes storedStats
public void AddStat(Stat newStat)
{
lock(ThreadLocker)
{
storedStats.Add(newStat);
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,该Checkpoint脚本现在将 设为storedStats private。我还添加了一些示例方法来修改或写入该底层storedStats. 因此,对它的所有storedStats访问都受到控制,并且线程都是同步的。这样,您的后台线程就无法在storedStats集合上迭代(因为它有一个副本,并且副本的创建是受控/线程安全的),并且对集合的任何写入都不能在另一个线程访问的同时发生它。
编辑:通过上述 API 更改,您的调用代码可能如下所示:
checkpointToText += EndRaceDataString (checkpoint.GetStoredStatsCopy());
Run Code Online (Sandbox Code Playgroud)