C# 为什么在WPF UI线程中执行异步回调
关于这段代码:C# 为什么在WPF UI线程中执行异步回调,c#,wpf,asynchronous,C#,Wpf,Asynchronous,关于这段代码: static async Task<string> testc() { Console.WriteLine("helo async " + Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("task " + Thread.CurrentThread.Manage
static async Task<string> testc()
{
Console.WriteLine("helo async " + Thread.CurrentThread.ManagedThreadId);
await Task.Run(() => {
Thread.Sleep(1000);
Console.WriteLine("task " + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("callback "+Thread.CurrentThread.ManagedThreadId);
return "bob";
}
static void Main(string[] args)
{
Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId);
testc();
Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.ReadLine();
}
哪一个是确定的:wait之后的一段代码与任务本身在同一个线程中执行
现在,如果我在WPF应用程序中执行此操作:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId);
testc();
Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.ReadLine();
}
它生成以下输出:
helo sync 10
helo async 10
over10
task 11
callback **11**
helo sync 8
helo async 8
over8
task 9
callback **8**
我们可以在UI线程中看到wait执行后的代码。好吧,这是伟大的,因为它可以操纵可观察的集合等。。。但我想知道“为什么?”“我怎么能做同样的事情?”这与TaskScheduler的某些行为有关吗?这是在.NET Framework中硬编码的吗
谢谢您提交的任何意见
但我想知道“为什么?”
你自己已经回答过了:
好吧,这是伟大的,因为它可以操纵可观察的集合等
async的全部目的是使异步更易于使用,这样您就可以编写实际上是异步的“同步外观”代码。这通常包括想要在一个上下文(例如,UI线程)中保留整个异步方法—当需要等待某个内容时,只需“暂停”该方法(而不阻塞UI线程)
“我怎么能这样做呢?”
不清楚你在这里是什么意思。基本上,Task
的等待模式的实现用于确定要在哪个调度程序上发布回调-除非您调用了该命令以明确选择退出此行为。所以这就是它管理它的方式。。。你是否能“做同样的事”取决于你到底想做什么
有关等待模式的更多详细信息,请参阅中的“什么是等待模式”问题。原因是该任务。从UI线程启动任务时,Run将捕获WPF应用程序中是否存在等待模式。然后,该任务将使用
SynchronizationContext
将回调序列化到UI线程。但是,如果没有上下文像控制台应用程序中那样是可唤醒的,那么回调将发生在不同的线程上
Stephen Toub在一篇文章中描述了这一点
顺便说一句,使用从不使用线程时要小心。在任务中睡眠。它可能会导致奇怪的行为,因为任务可能不会绑定到一个线程。改用Task.Delay。您可能会发现我的建议很有帮助。其他答案几乎是正确的
当您等待尚未完成的任务时,默认情况下会捕获一个“上下文”,用于在任务完成时恢复方法。此“上下文”为SynchronizationContext.Current
,除非它为null,在这种情况下,它是TaskScheduler.Current
请注意此操作所需的条件:
- “当您
如果您手动安排继续,例如使用等待时”
,则不会执行上下文捕获。您必须自己使用类似于任务.ContinueWith
(SynchronizationContext.Current==null?TaskSchedler.Current:TaskScheduler.FromCurrentSynchronizationContext())的方法来完成此操作。
- “..
aawait
..”-此行为是Task
类型的Task
行为的一部分。其他类型可能会也可能不会执行类似的捕获await
- “…尚未完成…”-如果
在等待时已完成,任务
方法将同步继续。因此,在这种情况下,不需要捕获上下文异步
- “…默认情况下…”-这是默认行为,可以更改。特别是,调用
方法并传递Task.configurewait
参数的continueOnCapturedContext
。此方法返回一个等待类型(不是false
),如果其参数为任务
,则该类型将不会在捕获的上下文上继续false