Ali*_*kar 5 c# xna multithreading file-management
我目前有一款游戏可以拍摄大于1MB的大图像作为背景.我确切知道何时应该进行这种转换,所以我创建了一个加载器类来处理在后台加载这些大图像,但是当我加载图像时它仍然会冻结绘图发生的主线程.由于此代码在360上运行,我将线程移动到第4个硬件线程,但这似乎没有帮助.以下是我正在使用的课程.任何想法为什么我的新内容管理器应该在自己的线程中打断我的主线程中的绘制将不胜感激.
namespace FileSystem
{
/// <summary>
/// This is used to reference how many objects reference this texture.
/// Everytime someone references a texture we increase the iNumberOfReferences.
/// When a class calls remove on a specific texture we check to see if anything
/// else is referencing the class, if it is we don't remove it. If there isn't
/// anything referencing the texture its safe to dispose of.
/// </summary>
class TextureContainer
{
public uint uiNumberOfReferences = 0;
public Texture2D texture;
}
/// <summary>
/// This class loads all the files from the Content.
/// </summary>
static class FileManager
{
static Microsoft.Xna.Framework.Content.ContentManager Content;
static EventWaitHandle wh = new AutoResetEvent(false);
static Dictionary<string, TextureContainer> Texture2DResourceDictionary;
static List<Texture2D> TexturesToDispose;
static List<String> TexturesToLoad;
static int iProcessor = 4;
private static object threadMutex = new object();
private static object Texture2DMutex = new object();
private static object loadingMutex = new object();
private static bool bLoadingTextures = false;
/// <summary>
/// Returns if we are loading textures or not.
/// </summary>
public static bool LoadingTexture
{
get {
lock (loadingMutex)
{
return bLoadingTextures;
}
}
}
/// <summary>
/// Since this is an static class. This is the constructor for the file loadeder. This is the version
/// for the Xbox 360.
/// </summary>
/// <param name="_Content"></param>
public static void Initalize(IServiceProvider serviceProvider, string rootDirectory, int _iProcessor )
{
Content = new Microsoft.Xna.Framework.Content.ContentManager(serviceProvider, rootDirectory);
Texture2DResourceDictionary = new Dictionary<string, TextureContainer>();
TexturesToDispose = new List<Texture2D>();
iProcessor = _iProcessor;
CreateThread();
}
/// <summary>
/// Since this is an static class. This is the constructor for the file loadeder.
/// </summary>
/// <param name="_Content"></param>
public static void Initalize(IServiceProvider serviceProvider, string rootDirectory)
{
Content = new Microsoft.Xna.Framework.Content.ContentManager(serviceProvider, rootDirectory);
Texture2DResourceDictionary = new Dictionary<string, TextureContainer>();
TexturesToDispose = new List<Texture2D>();
CreateThread();
}
/// <summary>
/// Creates the thread incase we wanted to set up some parameters
/// Outside of the constructor.
/// </summary>
static public void CreateThread()
{
Thread t = new Thread(new ThreadStart(StartThread));
t.Start();
}
// This is the function that we thread.
static public void StartThread()
{
//BBSThreadClass BBSTC = (BBSThreadClass)_oData;
FileManager.Execute();
}
/// <summary>
/// This thread shouldn't be called by the outside world.
/// It allows the File Manager to loop.
/// </summary>
static private void Execute()
{
// Make sure our thread is on the correct processor on the XBox 360.
#if WINDOWS
#else
Thread.CurrentThread.SetProcessorAffinity(new int[] { iProcessor });
Thread.CurrentThread.IsBackground = true;
#endif
// This loop will load textures into ram for us away from the main thread.
while (true)
{
wh.WaitOne();
// Locking down our data while we process it.
lock (threadMutex)
{
lock (loadingMutex)
{
bLoadingTextures = true;
}
bool bContainsKey = false;
for (int con = 0; con < TexturesToLoad.Count; con++)
{
// If we have already loaded the texture into memory reference
// the one in the dictionary.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(TexturesToLoad[con]);
}
if (bContainsKey)
{
// Do nothing
}
// Otherwise load it into the dictionary and then reference the
// copy in the dictionary
else
{
TextureContainer TC = new TextureContainer();
TC.uiNumberOfReferences = 1; // We start out with 1 referece.
// Loading the texture into memory.
try
{
TC.texture = Content.Load<Texture2D>(TexturesToLoad[con]);
// This is passed into the dictionary, thus there is only one copy of
// the texture in memory.
// There is an issue with Sprite Batch and disposing textures.
// This will have to wait until its figured out.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(TexturesToLoad[con]);
Texture2DResourceDictionary.Add(TexturesToLoad[con], TC);
}
// We don't have the find the reference to the container since we
// already have it.
}
// Occasionally our texture will already by loaded by another thread while
// this thread is operating. This mainly happens on the first level.
catch (Exception e)
{
// If this happens we don't worry about it since this thread only loads
// texture data and if its already there we don't need to load it.
}
}
Thread.Sleep(100);
}
}
lock (loadingMutex)
{
bLoadingTextures = false;
}
}
}
static public void LoadTextureList(List<string> _textureList)
{
// Ensuring that we can't creating threading problems.
lock (threadMutex)
{
TexturesToLoad = _textureList;
}
wh.Set();
}
/// <summary>
/// This loads a 2D texture which represents a 2D grid of Texels.
/// </summary>
/// <param name="_textureName">The name of the picture you wish to load.</param>
/// <returns>Holds the image data.</returns>
public static Texture2D LoadTexture2D( string _textureName )
{
TextureContainer temp;
lock (Texture2DMutex)
{
bool bContainsKey = false;
// If we have already loaded the texture into memory reference
// the one in the dictionary.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(_textureName);
if (bContainsKey)
{
temp = Texture2DResourceDictionary[_textureName];
temp.uiNumberOfReferences++; // Incrementing the number of references
}
// Otherwise load it into the dictionary and then reference the
// copy in the dictionary
else
{
TextureContainer TC = new TextureContainer();
TC.uiNumberOfReferences = 1; // We start out with 1 referece.
// Loading the texture into memory.
try
{
TC.texture = Content.Load<Texture2D>(_textureName);
// This is passed into the dictionary, thus there is only one copy of
// the texture in memory.
}
// Occasionally our texture will already by loaded by another thread while
// this thread is operating. This mainly happens on the first level.
catch(Exception e)
{
temp = Texture2DResourceDictionary[_textureName];
temp.uiNumberOfReferences++; // Incrementing the number of references
}
// There is an issue with Sprite Batch and disposing textures.
// This will have to wait until its figured out.
Texture2DResourceDictionary.Add(_textureName, TC);
// We don't have the find the reference to the container since we
// already have it.
temp = TC;
}
}
}
// Return a reference to the texture
return temp.texture;
}
/// <summary>
/// Go through our dictionary and remove any references to the
/// texture passed in.
/// </summary>
/// <param name="texture">Texture to remove from texture dictionary.</param>
public static void RemoveTexture2D(Texture2D texture)
{
foreach (KeyValuePair<string, TextureContainer> pair in Texture2DResourceDictionary)
{
// Do our references match?
if (pair.Value.texture == texture)
{
// Only one object or less holds a reference to the
// texture. Logically it should be safe to remove.
if (pair.Value.uiNumberOfReferences <= 1)
{
// Grabing referenc to texture
TexturesToDispose.Add(pair.Value.texture);
// We are about to release the memory of the texture,
// thus we make sure no one else can call this member
// in the dictionary.
Texture2DResourceDictionary.Remove(pair.Key);
// Once we have removed the texture we don't want to create an exception.
// So we will stop looking in the list since it has changed.
break;
}
// More than one Object has a reference to this texture.
// So we will not be removing it from memory and instead
// simply marking down the number of references by 1.
else
{
pair.Value.uiNumberOfReferences--;
}
}
}
}
/*public static void DisposeTextures()
{
int Count = TexturesToDispose.Count;
// If there are any textures to dispose of.
if (Count > 0)
{
for (int con = 0; con < TexturesToDispose.Count; con++)
{
// =!THIS REMOVES THE TEXTURE FROM MEMORY!=
// This is not like a normal dispose. This will actually
// remove the object from memory. Texture2D is inherited
// from GraphicsResource which removes it self from
// memory on dispose. Very nice for game efficency,
// but "dangerous" in managed land.
Texture2D Temp = TexturesToDispose[con];
Temp.Dispose();
}
// Remove textures we've already disposed of.
TexturesToDispose.Clear();
}
}*/
/// <summary>
/// This loads a 2D texture which represnets a font.
/// </summary>
/// <param name="_textureName">The name of the font you wish to load.</param>
/// <returns>Holds the font data.</returns>
public static SpriteFont LoadFont( string _fontName )
{
SpriteFont temp = Content.Load<SpriteFont>( _fontName );
return temp;
}
/// <summary>
/// This loads an XML document.
/// </summary>
/// <param name="_textureName">The name of the XML document you wish to load.</param>
/// <returns>Holds the XML data.</returns>
public static XmlDocument LoadXML( string _fileName )
{
XmlDocument temp = Content.Load<XmlDocument>( _fileName );
return temp;
}
/// <summary>
/// This loads a sound file.
/// </summary>
/// <param name="_fileName"></param>
/// <returns></returns>
public static SoundEffect LoadSound( string _fileName )
{
SoundEffect temp = Content.Load<SoundEffect>(_fileName);
return temp;
}
}
}
Run Code Online (Sandbox Code Playgroud)
来自博客文章:Shawn Hargreaves 的加载屏幕动画期间锁定争用
这里有龙!上面的代码可以工作,但可能会使您的加载速度慢数百倍。
原因是每次从不同的线程触摸图形设备时,框架都会取出一个关键部分,以避免出现heisenbadness。如果动画不断重绘,则动画线程几乎总是拥有此锁,因此每当您的加载函数想要创建图形资源(例如纹理、顶点缓冲区或着色器)时,它都必须等待动画线程释放它。如果动画线程不断循环遍历同一段绘制代码,这可能需要一段时间!
解决方案是通过插入 sleep 调用来减慢动画线程,这会阻止它占用图形设备。
但在你的情况下,因为你正在加载非常大的连续纹理,所以你可能只需要重新设计你的加载功能,以便在用户不需要看到任何动画时发生,因为你会期望有一个锁。
| 归档时间: |
|
| 查看次数: |
1697 次 |
| 最近记录: |