【问题标题】:Task.Delay(N) System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.' [duplicate]Task.Delay(N) System.InvalidOperationException: '调用线程必须是 STA,因为许多 UI 组件需要这个。' [复制]
【发布时间】:2023-03-30 19:31:01
【问题描述】:

我想通过执行以下操作从我的MainWindow's ViewModel 打开一个新的WPF window,延迟 2 秒:

await Task.Delay(2000).ContinueWith(_ =>
                    {
                        Restart wndRestart = new Restart();
                        wndRestart.Show();
                    }
                );

可悲的是,我不断收到:System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.'

我其实查了this,但是我重构了一个方法后还是无法使用[STAThread]

【问题讨论】:

  • 投票重新打开,因为链接的答案并没有真正解决 this 特定问题和所需的解决方案/解决方法。
  • “链接的答案并没有真正解决这个特定问题”——当然可以。或者至少,它解决了这个含糊不清的问题的一个例子。事实是,问题中没有足够的细节来知道出了什么问题。如果发布的代码首先在 UI 线程中执行,则根本不会发生异常。所以修复它的一种方法是在 UI 线程中执行它,每个副本。还有许多其他情况可能会导致异常,但在问题中没有任何证据适用。
  • 不要使用Task.ContinueWith。只需在await 之后继续您的代码。
  • 仅澄清这一点:Window 只能在 UI 线程上创建。

标签: c# wpf


【解决方案1】:

您可以将自己的同步上下文传递给ContinueWith,以告诉任务调度程序您的延续应该在哪里运行:

await Task.Delay(2000).ContinueWith(_ =>
                {
                    Restart wndRestart = new Restart();
                    wndRestart.Show();
                },
                TaskScheduler.FromCurrentSynchronizationContext()
            );

否则,您将获得一个很可能不是 STA 的线程池线程。

但是,对于 WPF 中的 one-shot 在延迟后执行某些操作,也可以使用 DispatcherTimer

【讨论】:

  • 或者干脆不要在这种情况下使用Task.ContinueWith。由于您等待Task.Delay 并且Task 默认配置为ConfigureAwait(true),因此将在正确的线程(UI 线程)上继续执行。
  • 这是一个选项,如果目标是 .NET 4.5 及更高版本,是的。我曾经偶然发现过同样的问题,只是与 .NET 4.0 相关......
  • 基本上你是对的,但 Task.Delay 在 .NET 4.0 中不可用。 await 在此版本中也不可用。这意味着问题针对 .NET >= 4.5。使用Task.ContinueWith 在简单的await 之后继续执行是没有意义的,除非您想在工作线程上执行继续 - 这在 OP 的场景中显然不是这种情况。因此,放弃配置延续的线程池线程的开销似乎更明智。请注意,Task.Delay 是一种纯异步方法,它不在线程池线程上运行。
  • 好点。同意:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多