Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么在WPF UI线程中执行异步回调_C#_Wpf_Asynchronous - Fatal编程技术网

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())的方法来完成此操作。
  • “..
    await
    a
    Task
    ..”-此行为是
    Task
    类型的
    await
    行为的一部分。其他类型可能会也可能不会执行类似的捕获
  • “…尚未完成…”-如果
    任务
    在等待时已完成,
    异步
    方法将同步继续。因此,在这种情况下,不需要捕获上下文
  • “…默认情况下…”-这是默认行为,可以更改。特别是,调用
    Task.configurewait
    方法并传递
    continueOnCapturedContext
    参数的
    false
    。此方法返回一个等待类型(不是
    任务
    ),如果其参数为
    false
    ,则该类型将不会在捕获的上下文上继续

我想他所说的“我怎样才能做同样的事情?”的意思是:“我如何在不使用WPF的情况下获得WPF行为?”@jdv Jandavan:如果这是意思,那就非常不清楚了。无论如何,希望我所提供的细节能提供足够的信息,让这次行动更进一步。如果有必要,他们可以随时询问更多的细节。。好的我真的不想这么做。我想知道如何将回调发送到Dispatcher.Invoke方法。。。如果我最终能做类似的事情,或者如果它是在框架中硬编码的。谢谢你的回答谢谢你完整的回答。大部分内容(或者至少足以回答我的问题)都在@Jakob Christensen的答案中的链接中,但我很欣赏这个完整的观点!