C# 等待背景任务完成

C# 等待背景任务完成,c#,asynchronous,async-await,windows-10,win-universal-app,C#,Asynchronous,Async Await,Windows 10,Win Universal App,我有一个BackgroundTask,它生成一些文本,然后将其作为文件保存到LocalFolder中。我需要在BackgroundTask生成此文件后,将其与我的主项目(相同的VS解决方案)一起获取,并对其进行一些后续工作。后台任务由用户手动触发(通过“重新加载”按钮),并由TimeTrigger每隔15分钟触发一次 这是相关的代码片段: syncTrigger.RequestAsync(); articles = await getCachedArticles(""); 如何让getCac

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

这是相关的代码片段:

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

如何让
getCachedArticles
方法在上一个请求完成后再运行?多谢各位

如果希望当前上下文在继续之前等待异步方法完成,则需要使用
wait
关键字:

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

我建议您通读以全面了解其工作原理。

您需要等待RequestAsync方法完成并获得结果

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

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

如果我理解正确,你需要做的就是等待启动

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

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;
}
请注意,代码在调用
RequestAsync()
之前调用
CompletedAsync()
,以确保在触发任务之前注册偶数处理程序,以避免在注册处理程序之前任务完成时出现争用情况。

编辑:显示了最佳方法,我甚至为此编写了一篇文章。下面是我的原始答案,其中包含了一些可能适用于某些情况的间接备选方案

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

当您从应用程序触发后台任务时,您可以创建一个脚本使其工作。应用程序服务的行为类似于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
}
有关应用程序服务的更多信息,请查看

您也可以从计划的后台任务调用相同的应用程序服务,但在本例中,处理完成时无法获得通知

由于您提到您正在通过
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
}
据我所知,您只能监视文件夹中的所有更改,无法确定事件处理程序中发生了什么更改,因此对于您的情况,最好有一个单独的子文件夹,其中只包含后台任务完成后保存的文件。这样,只有在需要时才会引发事件


不过,您必须自己检查此方法是否足够可靠。

如果我正确理解您的问题,您希望
getCachedArticles(“”
仅在
syncTrigger.RequestAsync()完成后运行?对吗?没错。如果我的解释不是最好的,请原谅——我不是每天都说英语的人。:)没问题!只是想确定我理解了这个问题。我也不是母语人士,我理解这场斗争。你能展示一下
RequestAsync
getCachedArticles
的签名吗?对于
getCachedArticles()
来说,它是
私有异步任务getCachedArticles(string sourceName)
。不幸的是,即使是“等待”也没有改变。我在
articles=await-getCachedArticles(“”)上有断点
和我正在使用syncTrigger.RequestAsync()调用的BackgroundTask中,在请求调用之前执行
getCachedArticles()
RequestAsync
方法的方法签名是什么?非常奇怪的是,
articles=…
行应该运行--
await syncTrigger…
应该完全阻止这种情况,至少在它运行完之前是这样。我也希望如此。对不起,您所说的“方法签名”是什么意思?syncTrigger是一个
DeviceTriggerResult
?我所说的方法签名是指,例如
public returnType methodName(var input1,var input2)
。问题是,它会等到触发请求完成,而不是任务执行完成。你真的应该回去更改你的应用程序。我认为这根本不是一个非常可靠的解决方案。目前还没有一个可靠的解决方案。我真的很高兴看到你最后一次提到的观点——变更监控。我会试试……:)剩下的部分似乎太复杂了——我对UWP和C#都是新手。谢谢,你忽略了吗?或者你是说这是错的?@svick我认为你的答案不适用于每15分钟自动触发一次的预定任务?@DamirArh为什么不?它仍然是同一个后台任务,只是一个不同的触发器,因此相同的
BackgroundTaskRegistration
应该对两者都有效。@svick-Hm。。。只要您在应用程序启动时重新注册它,并附加一个新的事件处理程序(无论如何,这都是一个好主意),它就确实可以工作。当然,
TaskCompletionSourc
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
}