Async/Await 和 CreatePeriodicTimer 失败
Async/Await and CreatePeriodicTimer failing
我有一个执行以下操作的应用程序:
初始化连接的网络摄像头
private async Task<MediaCapture> InitialiseWebCam()
{
MediaCapture webCam = null;
webCam = new MediaCapture();
try
{
await webCam.InitializeAsync();
}
catch (Exception ex)
{
//Log error
}
return webCam;
}
拍照
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;
}
将图像上传到 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;
}
三个方法调用如下:
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 和 image 正在按预期工作,应用程序突然退出。
出现上述行为的原因是什么?我期待图像被上传并且整个过程继续在创建的计时器循环内发生。
编辑:
我试图通过像这样调整 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(),也会立即执行 returns 并且应用程序退出。
如果我删除 ThreadPoolTimer.CreatePeriodicTimer 的用法,并将其替换为 while 循环,则图像上传不会出现问题。
异步方法的问题是它们倾向于爬上你的代码并侵入它周围:)...你必须使用 await
关键字而不是调用 Result
属性,否则在尝试获取结果之前,您将无法确定该函数是否确实完成了执行。这适用于您代码中的所有异步方法。
将您的 Monitor 方法更改为异步和 return Task
,如下所示:
private async Task Monitor()
{
var webCam = await InitialiseWebCam();
var image = await TakePicture(webCam);
var output = await UploadPictureToAzure(image);
}
我猜 Windows phone 在顶层有一个异步 API,所以你可以等待到主线程,但如果没有,here are a couple of workarounds to call async methods synchronously
编辑:
根据 Quickstart: Create and register a background task (XAML),您可以在 运行 方法上执行以下操作:
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();
ThreadPoolTimer.CreatePeriodicTimer(async(timer) => {
await Monitor();
_deferral.Complete();
}, TimeSpan.FromSeconds(60));
}
最后我使用下面的代码实现了我所需要的:
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;
}
}
我有一个执行以下操作的应用程序:
初始化连接的网络摄像头
private async Task<MediaCapture> InitialiseWebCam() { MediaCapture webCam = null; webCam = new MediaCapture(); try { await webCam.InitializeAsync(); } catch (Exception ex) { //Log error } return webCam; }
拍照
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; }
将图像上传到 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; }
三个方法调用如下:
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 和 image 正在按预期工作,应用程序突然退出。
出现上述行为的原因是什么?我期待图像被上传并且整个过程继续在创建的计时器循环内发生。
编辑:
我试图通过像这样调整 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(),也会立即执行 returns 并且应用程序退出。
如果我删除 ThreadPoolTimer.CreatePeriodicTimer 的用法,并将其替换为 while 循环,则图像上传不会出现问题。
异步方法的问题是它们倾向于爬上你的代码并侵入它周围:)...你必须使用 await
关键字而不是调用 Result
属性,否则在尝试获取结果之前,您将无法确定该函数是否确实完成了执行。这适用于您代码中的所有异步方法。
将您的 Monitor 方法更改为异步和 return Task
,如下所示:
private async Task Monitor()
{
var webCam = await InitialiseWebCam();
var image = await TakePicture(webCam);
var output = await UploadPictureToAzure(image);
}
我猜 Windows phone 在顶层有一个异步 API,所以你可以等待到主线程,但如果没有,here are a couple of workarounds to call async methods synchronously
编辑:
根据 Quickstart: Create and register a background task (XAML),您可以在 运行 方法上执行以下操作:
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();
ThreadPoolTimer.CreatePeriodicTimer(async(timer) => {
await Monitor();
_deferral.Complete();
}, TimeSpan.FromSeconds(60));
}
最后我使用下面的代码实现了我所需要的:
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;
}
}