我想在 Unity 中从磁盘加载一堆图像。所以我想使用线程池在辅助线程中实际加载图像字节,然后在主线程中应用纹理。
我创建了一个函数,它获取一个路径作为输入并在 System.Threading.ThreadPool.QueueUserWorkItem
的帮助下加载图像字节,当它完成时它调用一个创建纹理的回调这些字节并将其应用于游戏对象。
我遇到的问题是回调是通过工作线程的关联执行的。 unity 不允许非主线程对其数据进行修改。
有没有办法在主线程的affinity中执行回调?
这是相关代码:
public delegate void OnBytesLoaded(byte[] bytes);
private void LoadBytesToTexture(byte[] bytes)
{
Texture2D texture = new Texture2D(100, 100);
texture.LoadImage(bytes);
thumbnail.texture = texture;
}
private void LoadTextureImage(string imagePath)
{
OnBytesLoaded callback = new OnBytesLoaded(LoadBytesToTexture);
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
byte[] bytes = System.IO.File.ReadAllBytes(imagePath);
Debug.Log($"Loaded image bytes");
callback?.Invoke(bytes);
});
}
正如我所说,我的问题是 LoadBytesToTexture
在线程池线程而非主线程的关联中执行。
最佳答案
在 .NET 中有一个概念叫做 SynchronizationContext .
简而言之,同步上下文可以委托(delegate)工作给它有关联的线程(根据框架,这可以是线程池 、GUI 线程、创建同步上下文的线程 ... 等等)。
使用这样的东西应该可以完成工作:
private void LoadTextureImage(string imagePath)
{
var syncContext = SynchronizationContext.Current;
OnBytesLoaded callback = new OnBytesLoaded(bytes => syncContext .Post(_ => LoadBytesToTexture(bytes), null));
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
byte[] bytes = System.IO.File.ReadAllBytes(imagePath);
Debug.Log($"Loaded image bytes");
callback?.Invoke(bytes);
});
}
注意:
SynchronizationContext
取决于调用上下文。
在当前场景中,syncContext
在 bytes => syncContext.Post
的闭包中捕获,这样
.Current
属性将返回 syncContext
将工作委托(delegate)回 GUI 线程(因为 LoadTextureImage
是从 < strong>GUI线程),
否则,如果我们使用了 bytes => SynchronizationContext.Current.Post
这可能会产生 null 或 syncContext
将工作委托(delegate)回 ThreadPool(因为 .Current
是从池中的线程调用的)。
要求从以下评论中获取更多信息:
OnBytesLoaded(bytes => syncContext .Post(_ => LoadBytesToTexture(bytes), null))
这个
OnBytesLoaded callback = new OnBytesLoaded(LoadBytesToTexture);
与
相同
OnBytesLoaded callback = new OnBytesLoaded(bytes => LoadBytesToTexture(bytes));
在代码的原始版本中,您使用 LoadBytesToTexture
作为 OnBytesLoaded
的委托(delegate)(它需要具有以下定义的方法/委托(delegate) byte[] -> void), 现在我们正在传递新的委托(delegate),它将使用 syncContext
将 LoadBytesToTexture
排队返回到 GUI 线程,它再次具有相同的定义 bytes [] -> 作废。
我们不是直接调用 LoadBytesToTexture
,而是告诉 syncContext
使用在此调用中传递的字节 callback?.Invoke(bytes)
。
关于C# 在主线程关联中执行 Threading.ThreadPool.QueueUserWorkItem 回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58363535/