2016-01-30 40 views
1

考虑下面的方法,例如:为什么新线程可以访问UI?

private async void F() 
{ 
    button.IsEnabled = false; 
    await Task.Delay(1000); 
    button.IsEnabled = true; 
} 

在这种情况下,开始在await任何代码总是发生在另一个线程(编辑:错误的)这大概不应该访问到UI线程,类似于桌面应用程序。在类似的情况下,我记得有一个例外,如:

该应用程序调用了一个接口,该接口被编组为另一个线程。

但是,例如不引发任何异常。这是预期的吗?我可以可靠地编写这样的代码吗?在UI线程上运行

回答

2

在开始伺机始终任何代码出现在另一个线程(非UI线程,对吧?),

没有,一点都没有。 await不会启动其他线程。我有一个async intro,如果您发现这种说法混淆,可能会有所帮助。

什么await做的是调度的方法的其余部分继续要运行的异步操作完成后(在这种情况下,异步操作仅仅是一个计时器射击)。默认情况下,await将捕获“上下文”,这是SynchronizationContext.Current(或者,如果是null,上下文是TaskScheduler.Current)。在这种情况下,有一个UI SynchronizationContext,确保了该async方法的其余部分将在UI线程上运行。

+0

为了确定,不'Task.Delay'开始一个新的线程?我记得有人争论的SO上的一个线索。 – Avenicci

+0

@Avenicci:没有。它只是启动一个计时器。我上次检查时,每个应用程序都有一个线程处理所有计时器。当定时器完成时,它的回调函数会在一个线程池线程上调用,该线程池非常简单地用于完成从Task.Delay返回的Task。但是没有创建新线程。 –

+0

如果'Task.Delay'使用相同的线程,异步方法如何直接终止? – Avenicci

1

代码有一个SynchronizationContext。您可以通过打印SynchronizationContext.Current来查看。在等待上下文被捕获之前,等待你的代码在该上下文中恢复,确保继续在UI线程上运行。

为了让您引用其中的延续是一个ThreadPool线程运行,你可以通过使用ConfigureAwait(false)禁用SynchronizationContext捕获的行为,:

private async void FooAsync() 
{ 
    button.IsEnabled = false; 
    await Task.Delay(1000).ConfigureAwait(false); 
    button.IsEnabled = true; 
} 

此代码将提高你期望的异常。

这个预期?我可以可靠地编写这样的代码吗?

是的,是的。使用async-await的代码默认会“做正确的事情”。但是,如果您确实想要将某些内容卸载到ThreadPool线程,则可以使用Task.Run

+0

我想我是错的是超越'await'代码在不同的线程运行,因此而是它发布到'SynchronizationContext',由UI线程使用? – Avenicci

+1

@Avenicci Task.Delay完成后的延续发布到SC,确保它在UI线程上运行。 – i3arnon

相关问题