public void button_push()
{
genericMethodAsync();
SynchronousCode1();
}
public async void genericMethodAsync()
{
await someOtherAsyncMethod();
SynchronousCode2();
SyncronousCode3();
}
假设我有一个调用异步方法的按钮,但是没有等待它。但是,按钮调用的异步方法确实有一个等待。
我假设因为按钮单击不会等待该方法,所以SynchronousCode1()
可能会在SynchronousCode2()
之前执行。
比方说,等待的异步方法需要很长时间,例如2秒,并且是IO绑定的任务。 SynchronousCode2()
方法是否将在与调用button_push()
方法相同的线程(或我说相同的上下文)上完成?
我不确定,因为button_Push()
命令可能会在异步方法中的等待完成之前运行完成。
答案 0 :(得分:2)
我认为,因为单击按钮不会等待该方法,所以SynchronousCode1()可能在SynchronousCode2()之前执行。
正确
比方说,等待的异步方法需要很长时间,例如2秒,并且是IO绑定的任务。 在与调用button_push()方法相同的线程(或者我应该说相同的上下文)上完成SynchronousCode2()方法吗?
button_push
方法中的所有内容都将在同一线程上执行。因为它从不使用await
,所以从不产生控制权。它所做的只是将新任务发布到队列中,然后继续执行其其余代码。
对于SynchronousCode2()
,是的,它有可能在不同的线程或不同的上下文中执行。这完全取决于同步上下文的工作方式。对于控制台应用程序(除线程池外没有同步上下文),它很可能位于其他线程上。在.NET框架上运行的ASP.NET应用程序中,它将位于相同的线程(受thread agility约束)和相同的上下文中。在.NET Core应用程序it could be different中。在WinForms应用程序(默认消息泵提供同步)中,该应用程序将是相同的。
答案 1 :(得分:0)
ConfigureAwait
)行为将取决于当前的SynchronizationContext:
此类所实现的同步模型的目的是允许公共语言运行时的内部异步/同步操作在不同的同步模型下正常运行。
ConfigureAwait
使用ConfigureAwait
,您可以影响延续行为。
参数
continueOnCapturedContext
布尔
true
尝试将继续情况编组回捕获的原始上下文;否则为false
。
您的示例可能来自WPF应用,并且将使用DispatcherSynchronizationContext
。这是一段
Parallel Computing - It's All About the SynchronizationContext。
DispatcherSynchronizationContext (WindowsBase.dll:System.Windows.Threading)WPF和Silverlight应用程序使用DispatcherSynchronizationContext,该代理将UI线程的Dispatcher的委托以“普通”优先级排队。当线程通过调用Dispatcher.Run开始其Dispatcher循环时,会将此SynchronizationContext作为当前上下文安装。 DispatcherSynchronizationContext的上下文是单个UI线程。
所有排队到DispatcherSynchronizationContext的委托都由特定的UI线程一次按其排队的顺序执行。当前的实现为每个顶级窗口创建一个DispatcherSynchronizationContext,即使它们都共享相同的基础Dispatcher。
.ConfigureAwait
在没有.ConfigureAwait(false)
SynchronousCode2
的WPF应用程序的上下文中,将在同一线程中执行。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Print($"SynchronizationContext.Current: {SynchronizationContext.Current}");
Print("\t button_push START");
genericMethodAsync();
Print("\t button_push END");
}
public async void genericMethodAsync()
{
Print("\t\t genericMethodAsync START");
await someOtherAsyncMethod();
Print("\t\t genericMethodAsync calling SynchronousCode2");
SynchronousCode2();
Print("\t\t genericMethodAsync END");
}
private void SynchronousCode2()
{
Print("\t\t\t SynchronousCode2 START");
Print("\t\t\t SynchronousCode2 END");
}
private async Task someOtherAsyncMethod()
{
Print("\t\t\t someOtherAsyncMethod START");
await Task.Delay(TimeSpan.FromSeconds(2));
Print("\t\t\t someOtherAsyncMethod END");
}
private static void Print(string v) =>
Console.WriteLine($"T{System.Threading.Thread.CurrentThread.ManagedThreadId}: {v}");
}
T1: SynchronizationContext.Current: System.Windows.Threading.DispatcherSynchronizationContext
T1: button_push START
T1: genericMethodAsync START
T1: someOtherAsyncMethod START
T1: button_push END
T1: someOtherAsyncMethod END
T1: genericMethodAsync calling SynchronousCode2
T1: SynchronousCode2 START
T1: SynchronousCode2 END
T1: genericMethodAsync END
.ConfigureAwait
如果使用ConfigureAwait(false)
,则SynchronousCode2
可能被另一个线程调用。
await someOtherAsyncMethod().ConfigureAwait(continueOnCapturedContext: false);
输出
T1: SynchronizationContext.Current: System.Windows.Threading.DispatcherSynchronizationContext
T1: button_push START
T1: genericMethodAsync START
T1: someOtherAsyncMethod START
T1: button_push END
T1: someOtherAsyncMethod END
T9: genericMethodAsync calling SynchronousCode2
T9: SynchronousCode2 START
T9: SynchronousCode2 END
T9: genericMethodAsync END
在控制台应用程序的上下文中。结果将有所不同。 SynchronousCode2
可能会或可能不会由同一线程执行。
public void button_push()
{
Print("\t button_push START");
genericMethodAsync();
Print("\t button_push END");
}
public async void genericMethodAsync()
{
Print("\t\t genericMethodAsync START");
await someOtherAsyncMethod();
Print("\t\t genericMethodAsync calling SynchronousCode2");
SynchronousCode2();
Print("\t\t genericMethodAsync END");
}
private void SynchronousCode2()
{
Print("\t\t\t SynchronousCode2 START");
Print("\t\t\t SynchronousCode2 END");
}
private async Task someOtherAsyncMethod()
{
Print("\t\t\t someOtherAsyncMethod START");
await Task.Delay(TimeSpan.FromSeconds(2));
Print("\t\t\t someOtherAsyncMethod END");
}
private static void Print(string v) =>
Console.WriteLine($"T{System.Threading.Thread.CurrentThread.ManagedThreadId}: {v}");
static void Main(string[] args)
{
Print(" Main START");
new Program().button_push();
Print(" Main after button_push");
Console.ReadLine();
}
// .NETCoreApp,Version=v3.0
T1: Main START
T1: SynchronizationContext.Current: null
T1: button_push START
T1: genericMethodAsync START
T1: someOtherAsyncMethod START
T1: button_push END
T1: Main after button_push
T4: someOtherAsyncMethod END
T4: genericMethodAsync calling SynchronousCode2
T4: SynchronousCode2 START
T4: SynchronousCode2 END
T4: genericMethodAsync END
async avoid
通常应避免使用async void
,但事件处理程序是例外。
另一个非常重要的规则是不要阻塞UI线程。
您可以使button_push
async
,使genericMethodAsync
返回Task
,并使事情更可预测。
public async void button_push()
{
await genericMethodAsync();
SynchronousCode1();
}
async Task genericMethodAsync()
{
await someOtherAsyncMethodAsync();
SynchronousCode2();
SyncronousCode3();
}
答案 2 :(得分:0)
我认为由于按钮单击不会等待该方法,因此
SynchronousCode1()
可能会在SynchronousCode2()
之前执行。
是的,这是正确的。这将取决于someOtherAsyncMethod()
的实现。但是,假设该方法不能同步完成(如其权利),那么它将最终产生,从而使await someOtherAsyncMethod()
产生给调用者,这将允许在可能之前调用SynchronousCode1()
方法someOtherAsyncMethod()
方法已经完成(并且肯定在此之前,只要someOtherAsyncMethod()
的异步完成有相当长的时间)。
比方说,等待的异步方法需要很长时间,例如2秒,并且是IO绑定的任务。 在与调用button_push()方法相同的线程(或者我应该说相同的上下文)上完成SynchronousCode2()方法吗?
鉴于您发布的代码,并假设代码是在典型的UI线程(即具有线程特定同步上下文的UI线程)中执行的,那么,SynchronousCode2()
方法将在最初调用button_push()
方法的线程。