[英]Unity3d and Threading
我想在我的应用程序中做一些耗时但对实际游戏没有影响的东西。 因此,我试图使用一个线程分别运行这些东西。 以下是我创建线程的方法:
Thread thread = new Thread(() => WriteCheckpointTextFile (myFileName, checkpoint));
thread.Start();
这是我在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");
}
现在出于某种原因,上次调试并不总是打印出来,并且我的文件并不总是被写入。 可能是什么导致了这个?
问题是你的集合checkpoint.storedStats
正在从另一个线程(可能你是主线程)被修改,而你在后台线程中迭代它。 这会抛出InvalidOperationException
。 因为您的代码是在后台线程上执行的,所以Unity播放器/引擎会以静默方式抛出,未处理并忽略该异常。
不幸的是,这意味着您必须同步或限制对checkpoint.storedStats
集合的访问和更改,以便只有1个线程可以对其进行修改,并且在执行此操作时,其他线程不得对其进行迭代。
很难对您实施的最佳或最简单的方法发表评论,因为我不知道您在整个应用程序中如何使用storedStats
集合。 一种可能的,相当典型的解决方案是使用lock
语句包装所有位置访问集合。 这将确保在任何给定时间只有1个线程进入锁定块。 然后,当访问storedStats
进行读取 (而不是写入 )时,您可以在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);
}
}
}
请注意, Checkpoint
脚本现在使storedStats
私有 。 我还添加了一些修改或写入底层storedStats
示例方法。 因此,对该storedStats
所有访问storedStats
受到控制,并且线程也是同步的。 这样你的后台线程就不能在storedStats
集合上进行迭代(因为它有一个副本,并且副本的创建是受控/线程安全的),并且对集合的任何写入都不能与另一个线程同时发生。访问它。
编辑:通过上述API更改,您的调用代码可能如下所示:
checkpointToText += EndRaceDataString (checkpoint.GetStoredStatsCopy());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.