异步读取图像文件

时间:2018-09-16 20:10:46

标签: 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....
    }

1 个答案:

答案 0 :(得分:0)

您不需要Task来异步加载图像。您可以使用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完成,该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);
            });
        }
    });
}