Async / Await和CreatePeriodicTimer失败

时间:2015-09-22 21:05:47

标签: c# async-await windows-10-iot-core

我有一个执行以下操作的应用程序:

  1. 初始化连接的网络摄像头

    private async Task<MediaCapture> InitialiseWebCam()
    {
        MediaCapture webCam = null;
        webCam = new MediaCapture();
    
        try
        {
            await webCam.InitializeAsync();
        }
        catch (Exception ex)
        {
            //Log error
        }
    
        return webCam;
    }
    
  2. 拍摄图像

    private async Task<StorageFile> TakePicture(MediaCapture webCam)
    {
        try
        {
            var image = await KnownFolders.PicturesLibrary.CreateFileAsync("TestImage.jpg", CreationCollisionOption.GenerateUniqueName);
    
            var imageEncodingProperties = ImageEncodingProperties.CreatePng();
            await webCam.CapturePhotoToStorageFileAsync(imageEncodingProperties, image);
    
            return image;
        }
        catch (Exception ex)
        {
            //Log error
        }
        return null;
    }
    
  3. 将图像上传到Azure Blob存储

    private async Task<StorageFile> UploadPictureToAzure(StorageFile image)
    {
    string Azure_StorageAccountName = "MYACCOUNTNAME";
    string Azure_ContainerName = "MYCONTAINERNAME";
    string Azure_AccessKey = "MYACCESSKEY";
    
        try
        {
            StorageCredentials creds = new StorageCredentials(Azure_StorageAccountName, Azure_AccessKey);
            CloudStorageAccount account = new CloudStorageAccount(creds, useHttps: true);
            CloudBlobClient client = account.CreateCloudBlobClient();
            CloudBlobContainer sampleContainer = client.GetContainerReference(Azure_ContainerName);
    
            CloudBlockBlob blob = sampleContainer.GetBlockBlobReference(image.Name);
    
            await blob.UploadFromFileAsync(image);
    
            return image;
        }
        catch (Exception ex)
        {
            //Log
        }
    
        return image;
    }
    
  4. 调用这三种方法如下:

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        Monitor();
    }
    
    private void Monitor()
    {
        var webCam = InitialiseWebCam().Result;
        var image = TakePicture(webCam).Result;
    
        var output = UploadPictureToAzure(image).Result;
    }
    

    此时一切正常,图像出现在我的blob存储中。我希望此行为作为Windows IoT上的无头后台应用程序运行,因此我进行了以下调整:

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        taskInstance.GetDeferral();
    
        timer = ThreadPoolTimer.CreatePeriodicTimer(Timer_Tick, TimeSpan.FromSeconds(60));
    }
    
    private void Timer_Tick(ThreadPoolTimer threadPoolTimer)
    {
        Monitor();
    }
    

    根据这些更改,Monitor功能会按预期触发,但UploadPictureToAzure(image).Result的输出为空,即使 webCam 图像按预期工作,应用程序突然退出。

    上述行为的原因是什么?我期待上传图像,整个过程继续在创建的定时器循环中发生。

    修改

    我试图通过调整我的UploadPictureToAzure来强制同步行为:

    private void UploadPictureToAzure(StorageFile image)
    {
        string Azure_StorageAccountName = "ACCOUNTNAME";
        string Azure_ContainerName = "CONTAINERNAME";
        string Azure_AccessKey = "ACCESSKEY";
    
        try
        {
             StorageCredentials creds = new StorageCredentials(Azure_StorageAccountName, Azure_AccessKey);
             CloudStorageAccount account = new CloudStorageAccount(creds, useHttps: true);
             CloudBlobClient client = account.CreateCloudBlobClient();
             CloudBlobContainer sampleContainer = client.GetContainerReference(Azure_ContainerName);
    
             CloudBlockBlob blob = sampleContainer.GetBlockBlobReference(image.Name);
    
             var response = blob.UploadFromFileAsync(image);
             response.AsTask().Wait();
         }
         catch (Exception ex)
         {
             //Log
         }
    }
    

    现在即使调用 .Wait(),执行也会立即返回并退出应用程序。

    如果我删除 ThreadPoolTimer.CreatePeriodicTimer 的使用情况,并将其替换为while循环,则图片上传时不会出现问题。

2 个答案:

答案 0 :(得分:0)

使用异步方法的方法是,他们倾向于抓取您的代码并将其全部入侵:)...您必须使用await关键字而不是调用Result属性,否则在尝试获取结果之前,您无法确定该函数是否已完成执行。这适用于代码中的所有异步方法。

将您的Monitor方法更改为异步并返回Task,如下所示:

private async Task Monitor()
{
    var webCam = await InitialiseWebCam();
    var image = await TakePicture(webCam);
    var output = await UploadPictureToAzure(image);
}

我猜测Windows手机在顶层有一个异步API,所以你可以等到主线程,但如果没有,here are a couple of workarounds to call async methods synchronously

编辑:

Quickstart: Create and register a background task (XAML)您可以在Run方法上执行以下操作:

public async void Run(IBackgroundTaskInstance taskInstance)
{
    BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();

    ThreadPoolTimer.CreatePeriodicTimer(async(timer) => { 
        await Monitor();
        _deferral.Complete();
    }, TimeSpan.FromSeconds(60));


}

答案 1 :(得分:-1)

最后,我能够使用以下代码实现我的需求:

private readonly TimeSpan _delayDuration = TimeSpan.FromMinutes(20);

public void Run(IBackgroundTaskInstance taskInstance)
{
    taskInstance.GetDeferral();

    do
    {
        try
        {
            Monitor().Wait();
        }
        /* SNIP */
    }
    while (true);

private async Task Monitor()
{
    var webCam = await InitialiseWebCam();
    var image = await TakePicture(webCam);
    await UploadPictureToAzure(image);
    await Task.Delay(_delayDuration);
}


private async Task UploadPictureToAzure(StorageFile image)
    {
        var Azure_StorageAccountName = "accountname";
        var Azure_ContainerName = "container";
        var Azure_AccessKey = "key";
        try
        {
            StorageCredentials creds = new StorageCredentials(Azure_StorageAccountName, Azure_AccessKey);
            CloudStorageAccount account = new CloudStorageAccount(creds, true);
            CloudBlobClient client = account.CreateCloudBlobClient();
            CloudBlobContainer sampleContainer = client.GetContainerReference(Azure_ContainerName);
        CloudBlockBlob blob = sampleContainer.GetBlockBlobReference(image.Name);

        await blob.UploadFromFileAsync(image);
    }
    catch (Exception ex)
    {
        /* Logging */
        throw ex;
    }
}

private async Task<MediaCapture> InitialiseWebCam()
{
    var webCam = new MediaCapture();

    try
    {
        await webCam.InitializeAsync();
    }
    catch (Exception ex)
    {
       /* Logging */
       throw ex;
    }

    return webCam;
}

private async Task<StorageFile> TakePicture(MediaCapture webCam)
{
    try
    {
        var image = await KnownFolders.PicturesLibrary.CreateFileAsync("Image.png", CreationCollisionOption.ReplaceExisting);
        var imageEncodingProperties = ImageEncodingProperties.CreatePng();

        await webCam.CapturePhotoToStorageFileAsync(imageEncodingProperties, image);

        return image;
    }
    catch (Exception ex)
    {
        /* Logging */
        throw ex;
    }
}