等待BackgroundTask完成

时间:2016-02-20 23:04:32

标签: c# asynchronous async-await windows-10 win-universal-app

我有BackgroundTask生成一些文字,然后将其作为文件保存到LocalFolder。在BackgroundTask生成后,我需要使用我的主项目(相同的VS解决方案)获取此文件,并进行下一步的工作。后台任务由用户手动触发(通过"重新加载"按钮),每隔15分钟由TimeTrigger触发。

这是相关的代码段:

syncTrigger.RequestAsync(); 
articles = await getCachedArticles("");

如何告诉getCachedArticles方法在上一次请求完成之后才能运行?非常感谢你!

4 个答案:

答案 0 :(得分:4)

如果我理解正确,你需要做的就是等待the BackgroundTaskRegistration.Completed event解雇。

一种方法是创建一个返回Task的扩展方法,该方法在事件触发时完成:

public static Task<BackgroundTaskCompletedEventArgs> CompletedAsync(
    this BackgroundTaskRegistration registration)
{
    var tcs = new TaskCompletionSource<BackgroundTaskCompletedEventArgs>();

    BackgroundTaskCompletedEventHandler handler = (s, e) =>
    {
        tcs.SetResult(e);
        registration.Completed -= handler;
    };

    registration.Completed += handler;

    return tcs.Task;
}

然后你会像这样使用它:

var taskCompleted = registration.CompletedAsync();
await syncTrigger.RequestAsync();
await taskCompleted;
articles = await getCachedArticles("");

请注意,代码在调用CompletedAsync()之前调用RequestAsync() 以确保在触发任务之前注册偶数处理程序,以避免任务完成时的竞争条件在处理程序注册之前。

答案 1 :(得分:0)

每当您希望当前上下文在继续之前等待异步方法完成时,您希望使用await关键字:

await syncTrigger.RequestAsync(); //the line below will not be executed until syncTrigger.RequestAsync() completes its task
articles = await getCachedArticles("");

我建议您阅读await C# Reference Article,以全面了解其工作原理。

答案 2 :(得分:0)

编辑:svick's answer显示了最好的方法,我甚至写了一篇关于它的blog post。以下是我的原始答案,其中包含一些可能适用于某些情况的间接替代方案。

正如其他人所指出的那样,等待syncTrigger.RequestAsync()不会有帮助,尽管不过这是一个好主意。当后台任务被成功触发时,它将恢复执行,因此允许您检查它是否因任何原因失败。

当您从应用程序触发后台任务时,您可以创建app service以使其正常工作。应用服务的行为与Web服务类似。它们在后台任务中运行,但具有请求 - 响应语义。

在后台服务中,您需要处理RequestReceived事件:

public void Run(IBackgroundTaskInstance taskInstance)
{
    var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
    appServiceconnection = details.AppServiceConnection;
    appServiceconnection.RequestReceived += OnRequestReceived;
}

private async void OnRequestReceived(AppServiceConnection sender,
    AppServiceRequestReceivedEventArgs args)
{
    var messageDeferral = args.GetDeferral();

    ValueSet arguments = args.Request.Message;
    ValueSet result = new ValueSet();

    // read values from arguments
    // do the processing
    // put data for the caller in result

    await args.Request.SendResponseAsync(result);
    messageDeferral.Complete(); 
}

在客户端中,您可以调用应用服务:

var inventoryService = new AppServiceConnection();

inventoryService.AppServiceName = "from manifest";
inventoryService.PackageFamilyName = "from manifest";

var status = await inventoryService.OpenAsync();
var arguments = new ValueSet();
// set the arguments
var response = await inventoryService.SendMessageAsync(arguments);

if (response.Status == AppServiceResponseStatus.Success)
{
    var result = response.Message;
    // read data from the result
}

检查linked page以获取有关应用服务的更多信息。

您也可以从预定的后台任务中调用相同的应用服务,但在这种情况下处理完成时无法获得通知。

由于您已经提到您通过LocalFolder中的文件交换数据,您的应用可以尝试监控对该文件的更改

private async Task Init()
{
    var storageFolder = ApplicationData.Current.LocalFolder;
    var monitor = storageFolder.CreateFileQuery();
    monitor.ContentsChanged += MonitorContentsChanged;
    var files = await monitor.GetFilesAsync();
}

private void MonitorContentsChanged(IStorageQueryResultBase sender, object args)
{
    // react to the file change - should mean the background task completed
}

据我所知,您只能监视文件夹中的所有更改,并且无法确定事件处理程序内部的更改,因此对于您的情况,最好只包含一个单独的子文件夹后台任务完成后保存的文件。这样,只有在需要时才会引发事件。

但是,您必须亲自检查一下这种方法是否足够可靠。

答案 3 :(得分:-3)

您需要等待使用结果完成的RequestAsync方法 https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.background.devicetriggerresult

之后我建议用task.delay等几秒钟,然后尝试获取数据。

更新:  当您拥有设备触发结果时,您需要先检查此结果,然后尝试获取数据,之后您可以假设已保存数据。我之前建议使用Task.Delay只是等待几秒钟以确保所有数据都被保存,因为有时这个过程比预期的要多几毫秒。我这样做是因为我们没有像TriggerCompleted这样的事件我需要创建自己的方法。 我之前在我的应用程序中做过这个并且效果很好。