【问题标题】:C# return before await - without starting a new TaskC# 在等待之前返回 - 不启动新任务
【发布时间】:2015-10-25 08:15:36
【问题描述】:

这是针对用Xamarin 编写的iOS 应用程序。我所有的应用程序代码都在主线程中运行(即UI thread)。

UI 代码执行如下操作:

public async void ButtonClicked()
{
    StartSpinner();
    var data = await UpdateData();
    StopSpinner();
    UpdateScreen(data);
}

UpdateData 函数的作用如下:

public Task<Data> UpdateData()
{
   var data = await FetchFromServer();
   TriggerCacheUpdate();
   return data;
}

TriggerCacheUpdate 最终调用下面定义的函数

public Task RefreshCache()
{
    var data = await FetchMoreDataFromServer();
    UpdateInternalDataStructures();
}

我的问题是TriggerCacheUpdate 应该怎么写?要求是:

  1. 不能异步,我不想要UpdateData,因此 ButtonClicked 等待 RefreshCache 完成之前 继续。
  2. UpdateInternalDataStructures 需要在主 (UI) 线程上执行,即上面显示的所有其他代码执行的线程。

以下是我想出的一些替代方案:

public void TriggerCacheUpdate()
{
    RefreshCache();
}

上述方法有效,但会生成编译器警告。此外,来自RefreshCache 的异常处理不起作用。

public void TriggerCacheUpdate()
{
    Task.Run(async() =>
    {
        await RefreshCache();
    });
}

以上内容违反了要求 2,因为 UpdateInternalDataStructures 未与其他所有内容在同一线程上执行。

我认为可行的一种可能的替代方法是:

private event EventHandler Done; 
public void TriggerCacheUpdate()
{
    this.task = RefreshCache();
    Done += async(sender, e) => await this.task;
}

Task RefreshCache() {
    var data = await FetchMoreDataFromServer();
    UpdateInternalDataStructures();
    if (Done != null) {
       Done(this, EventArgs.Empty);
    }
}

上述方法有效吗?到目前为止,我的有限测试没有遇到任何问题。有没有更好的写法TriggerCacheUpdate?

【问题讨论】:

  • 为什么会被否决?如果问题不清楚,请添加评论。

标签: c# .net xamarin.ios async-await


【解决方案1】:

您的事件处理程序是异步的。这意味着,即使您等待任务完成,您的 UI 也会保持响应。因此,即使您等待 TriggerCacheUpdate 返回,您的 UI 也会保持响应。

但是,如果您确实确定您对 TriggerCachUpdate 的结果不感兴趣,那么您可以开始一个 Task 而无需等待它:

public Task<Data> UpdateData()
{
    var data = await FetchFromServer();
    Task.Run( () => TriggerCacheUpdate());
    return data;
}

注意:小心:您不知道 TriggerCachUpdate 何时完成,即使它成功结束或抛出异常也不知道。另外:检查如果在前一个尚未完成的情况下启动新的 TriggerCacheUpdate 任务会发生什么。

想要使用Task.Factory.StartNew的朋友,请看MSDN中的讨论:

Task.Run vs Task.Factory.StartNew

【讨论】:

  • 我想在 TriggerCacheUpdate 返回之前更新 UI。所以是的,它会保持响应,但它会继续显示微调器并且永远不会更新。 Task.Run(() => TriggerCacheUpdate());将导致 TriggerCacheUpdate 在单独的线程中运行,而不是在主线程中。
【解决方案2】:

无法测试很难说,但看起来您的触发器缓存更新方法很好,是您的 RefreshCache 需要更改。在 RefreshCache 内部,您不会在 UI 线程中等待“数据”的结果返回,因此请将 ConfigureAwait 设置为 false。

public async Task RefreshCache()
{
    var data = await FetchMoreDataFromServer().ConfigureAwait(false);
    UpdateInternalDataStructures();
}

【讨论】:

  • 为什么要设置 ConfigureAwait(false)?我希望 UpdateInternalDataStructures() 在主线程中执行。如果我设置了 ConfigureAwait(false),它可能会在任何线程中执行。
猜你喜欢
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-02
  • 1970-01-01
相关资源
最近更新 更多