c# - 异步读取图像文件

标签 c# .net unity3d uwp hololens

我想异步读取图像文件,我正在尝试使用 C# 中的 async/await 来实现。但是,我仍然注意到此实现存在明显滞后。使用探查器,我非常有信心是 FileStream.ReadAsync() 方法需要很长时间才能完成,而不是异步执行并停止游戏更新。

我注意到这种方式比仅使用 File.ReadAllBytes 更延迟,这很奇怪。我不能使用仍然会导致一些滞后的方法,我不想停止帧速率。

这是一些代码。

// Static class for reading the image.
class AsyncImageReader
{
    public static async Task<byte[]> ReadImageAsync(string filePath)
        {
            byte[] imageBytes;
            didFinishReading = false;
            using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
            {
                imageBytes = new byte[fileStream.Length];
                await fileStream.ReadAsync(imageBytes, 0, (int) fileStream.Length);

            }

            didFinishReading = true;

            return imageBytes;
        }
 }

    // Calling function that is called by the OnCapturedPhotoToDisk event. When this is called the game stops updating completely rather than 
    public void AddImage(string filePath)
    {
        Task<byte[]> readImageTask = AsyncImageReader.ReadImageAsync(filePath);
        byte [] imageBytes = await readImageTask;
        // other things that use the bytes....
    }

最佳答案

您不需要任务来异步加载图像。您可以使用 Unity 的 UnityWebRequestDownloadHandlerTexture API 加载。 UnityWebRequestTexture.GetTexture 简化了这一点,因为它会自动为您设置 DownloadHandlerTexture。这是在引擎盖下的另一个线程中完成的,但您使用协程来控制它。

public RawImage image;

void Start()
{
    StartCoroutine(LoadTexture());
}

IEnumerator LoadTexture()
{
    string filePath = "";//.....
    UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(filePath);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError || uwr.isHttpError)
    {
        Debug.Log(uwr.error);
    }
    else
    {
        Texture texture = ((DownloadHandlerTexture)uwr.downloadHandler).texture;
        image.texture = texture;
    }
}

您还可以使用 Thread API 在另一个线程中加载文件。更好的是,使用 ThreadPool API。他们中的任何一个都应该工作。加载图像字节数组后,要将字节数组加载到 Texture2D 中,您必须在主线程中执行此操作,因为您不能在主线程中使用 Unity 的 API。

要在加载完图像字节后从另一个线程使用 Unity 的 API,您需要一个包装器来执行此操作。下面的示例是使用 C# ThreadPool 和我的 UnityThread API 完成的,这简化了从另一个线程使用 Unity 的 API。你可以抢UnityThread从这里开始。

public RawImage image;

void Awake()
{
    //Enable Callback on the main Thread
    UnityThread.initUnityThread();
}

void ReadImageAsync()
{
    string filePath = "";//.....

    //Use ThreadPool to avoid freezing
    ThreadPool.QueueUserWorkItem(delegate
    {
        bool success = false;

        byte[] imageBytes;
        FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);

        try
        {
            int length = (int)fileStream.Length;
            imageBytes = new byte[length];
            int count;
            int sum = 0;

            // read until Read method returns 0
            while ((count = fileStream.Read(imageBytes, sum, length - sum)) > 0)
                sum += count;

            success = true;
        }
        finally
        {
            fileStream.Close();
        }

        //Create Texture2D from the imageBytes in the main Thread if file was read successfully
        if (success)
        {
            UnityThread.executeInUpdate(() =>
            {
                Texture2D tex = new Texture2D(2, 2);
                tex.LoadImage(imageBytes);
            });
        }
    });
}

关于c# - 异步读取图像文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52357990/

相关文章:

c# - 事务对象与连接对象不关联 - OracleClient

c# - for循环直接跳到最后

c# - 异步调用 wcf 4.5 WCF 服务

c# - 如何快速将 byte[] 转换为字符串?

c# mssql sql连接和多个插入

.net - 根据引用应用程序的位数适当加载 x64 或 x86 版本的程序集

c# - 在使用属性注入(inject)时,我们如何知道 "Local Defaults"是什么?

unity3d - Pro Grids 未在包管理器统一中显示

unity3d - 为什么 Collider2D.Raycast 和 Collider.Raycast 如此不同?

c# - 在 C# 中存储全局变量(如路径)的位置?