UI线程中Task.Wait的替代方案

时间:2012-11-16 06:04:50

标签: c# windows-8 microsoft-metro task-parallel-library async-ctp

我知道在UI线程中调用Task.Wait是不好的。它导致僵局。请参阅

Constructor invoke async method

Await, and UI, and deadlocks! Oh my!

请使用以下代码:

    public MainPage()
    {
        this.InitializeComponent();

        step0();
        step1();
    }

    private async Task step0()
    {
        await Task.Delay(5000);
        System.Diagnostics.Debug.WriteLine("step 0");
    }

    private async Task step1()
    {
        await Task.Delay(3000);
        System.Diagnostics.Debug.WriteLine("step 1");
    }
}

如何确保在“步骤1”之前始终打印“步骤0”?使用Task.Wait会导致死锁。 这是Windows应用商店应用

2 个答案:

答案 0 :(得分:8)

您可以使用Task.ContinueWith将任务链接在一起,以便它们按顺序发生。

public MainPage()
{
    this.InitializeComponent();

    step0().ContinueWith(t => step1());
}

此外,阿列克谢的评论是正确的。 Task.Wait阻止 UI线程。死锁是另一回事。

答案 1 :(得分:5)

您不能拥有async构造函数,句点。

所以无论你做什么,你都需要处理构造函数返回没有完成async初始化的情况。

有几种方法可以解决这个问题。如果“步骤”正在加载资源,那么您可以执行以下操作(使用AsyncLazy type from my blogStephen Toub's blog或我的AsyncEx library - 它们几乎相同):

private readonly AsyncLazy<MyResource> resource;

public MainPage()
{
  resource = new AsyncLazy<MyResource>(async () =>
  {
    var a = await step0();
    var b = await step1();
    return new MyResource(a, b); // or whatever
  });
  resource.Start(); // start step0
}

public async Task MethodThatNeedsResource()
{
  var r = await resource; // ensure step1 is complete before continuing
}

也可以使用async void方法或ContinueWith执行此操作,但您必须仔细考虑使用这些方法进行错误处理。

  • AsyncLazy方法会捕获错误,并在执行await resource时提升错误。
  • async void方法会立即将错误抛给SynchronizationContext
  • 天真的ContinueWith方法会默默地忽略所有错误。

无论您采取哪种方法,我建议在UI中通知初始化正在进行中。 (作为旁注,我认为所有这些应该进入ViewModel或Model类而不是MainPage)。