线程功能迁移
本主题介绍如何将通用 Windows 平台(UWP)应用程序中的线程代码迁移到Windows 应用 SDK。
API 和/或功能差异摘要
UWP 的线程模型是称为 Application STA(ASTA)的单线程单元(STA)模型的变体,它阻止重新进入,并帮助避免各种重新进入 bug 和死锁。 ASTA 线程也称为 UI 线程。
Windows 应用 SDK使用标准 STA 线程模型,该模型不提供相同的重新进入安全措施。
CoreDispatcher 类型会迁移到 DispatcherQueue。 并且 CoreDispatcher.RunAsync 方法也会迁移到 DispatcherQueue.TryEnqueue。
C++/WinRT。 如果将 winrt::resume_foreground 与 CoreDispatcher 一起使用,请将其迁移为改用 DispatcherQueue。
ASTA 到 STA 线程模型
有关 ASTA 线程模型的更多详细信息,请参阅博客文章关于应用程序 STA 的特别之处?
由于Windows 应用 SDK的 STA 线程处理模型在防止重新进入问题方面没有相同的保证,如果 UWP 应用假定 ASTA 线程模型的非重新加入行为,则代码可能无法按预期方式运行。
需要注意的一点是重新进入 XAML 控件(请参阅 UWP 照片编辑器示例应用 (C++/WinRT) 的 Windows 应用 SDK 迁移中的示例)。 对于某些崩溃(例如访问冲突),直接崩溃调用堆栈通常是要使用的正确堆栈。 但如果它是存放异常崩溃(异常代码为 0xc000027b),则需要更多的工作来获取正确的调用堆栈。
存放异常
存放异常崩溃保存了一个可能的错误,以后如果没有部分代码处理异常,系统便会使用该错误。 XAML 有时会立即判定错误是致命的,在这种情况下,直接崩溃堆栈可能是好的。 但更常见的情况是,堆栈在被确定为致命之前就已展开。 有关存放异常的详细信息,请参阅 Inside 节目剧集存放异常 C000027B。
对于存放异常崩溃,(查看嵌套的消息泵,或查看引发的 XAML 控件的特定异常)可以通过在 Windows 调试程序 (WinDbg) 中加载故障转储来获取有关崩溃的详细信息。请参阅下载适用于 Windows 的调试工具,然后使用 !pde.dse
来转储存放异常。
PDE 调试程序扩展(用于 !pde.dse
命令)可通过从 OneDrive 下载 PDE*.zip 文件获取。 将相应 x64 或 x86 .dll
从该 zip 文件放入 winext
WinDbg 安装的目录中,然后 !pde.dse
处理存存的异常故障转储。
通常会有多个存放异常,其中最后的一些异常已处理/已忽略。 最常见的情况是,第一个存放异常是耐人寻味的异常。 在某些情况下,第一个存放异常可能是再次引发的第二个异常,因此,如果第二个存放异常显示在与第一个相同的堆栈中更深的位置,那么第二个异常可能是错误的根源。 随每个存放异常一起显示的错误代码也很有用,因为它提供了与该异常关联的 HRESULT。
将 Windows.UI.Core.CoreDispatcher 更改为 Microsoft.UI.Dispatching.DispatcherQueue
如果 UWP 应用中使用的是 Windows.UI.Core.CoreDispatcher 类,则本部分适用。 这包括使用采用或返回 CoreDispatcher 的任何方法或属性,例如 DependencyObject.Dispatcher 和 CoreWindow.Dispatcher属性。 例如,在检索属于Windows.UI.Xaml.Controls.Page的 CoreDispatcher 时,将调用DependencyObject.Dispatcher。
// MainPage.xaml.cs in a UWP app
if (this.Dispatcher.HasThreadAccess)
{
...
}
// MainPage.xaml.cpp in a UWP app
if (this->Dispatcher().HasThreadAccess())
{
...
}
相反,在 Windows 应用 SDK 应用中,需要使用 Microsoft.UI.Dispatching.DispatcherQueue 类。 以及采用或返回 DispatcherQueue 的相应方法或属性,例如 DependencyObject.DispatcherQueue 和 Microsoft.UI.Xaml.Window.DispatcherQueue 属性。 例如,在检索属于 Microsoft.UI.Xaml.Controls.Page(大多数 XAML 对象都是 DependencyObject)的 DispatcherQueue 时,将调用DependencyObject.DispatcherQueue。
// MainPage.xaml.cs in a Windows App SDK app
if (this.DispatcherQueue.HasThreadAccess)
{
...
}
// MainPage.xaml.cpp in a Windows App SDK app
#include <winrt/Microsoft.UI.Dispatching.h>
...
if (this->DispatcherQueue().HasThreadAccess())
{
...
}
将 CoreDispatcher.RunAsync 更改为 DispatcherQueue.TryEnqueue
如果使用 Windows.UI.Core.CoreDispatcher.RunAsync 方法计划要在主 UI 线程(或与特定 Windows.UI.Core.CoreDispatcher 相关联的线程)上运行的任务,则本部分适用。
// MainPage.xaml.cs in a UWP app
public void NotifyUser(string strMessage)
{
if (this.Dispatcher.HasThreadAccess)
{
StatusBlock.Text = strMessage;
}
else
{
var task = this.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => StatusBlock.Text = strMessage);
}
}
// MainPage.cpp in a UWP app
void MainPage::NotifyUser(std::wstring strMessage)
{
if (this->Dispatcher().HasThreadAccess())
{
StatusBlock().Text(strMessage);
}
else
{
auto task = this->Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[strMessage, this]()
{
StatusBlock().Text(strMessage);
});
}
}
在 Windows 应用 SDK 应用中,则需改用 Microsoft.UI.Dispatching.DispatcherQueue.TryEnqueue 方法。 它会将要在与 DispatcherQueue 关联的线程上运行的任务添加到 Microsoft.UI.Dispatching.DispatcherQueue。
// MainPage.xaml.cs in a Windows App SDK app
public void NotifyUser(string strMessage)
{
if (this.DispatcherQueue.HasThreadAccess)
{
StatusBlock.Text = strMessage;
}
else
{
bool isQueued = this.DispatcherQueue.TryEnqueue(
Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal,
() => StatusBlock.Text = strMessage);
}
}
// MainPage.xaml.cpp in a Windows App SDK app
#include <winrt/Microsoft.UI.Dispatching.h>
...
void MainPage::NotifyUser(std::wstring strMessage)
{
if (this->DispatcherQueue().HasThreadAccess())
{
StatusBlock().Text(strMessage);
}
else
{
bool isQueued = this->DispatcherQueue().TryEnqueue(
Microsoft::UI::Dispatching::DispatcherQueuePriority::Normal,
[strMessage, this]()
{
StatusBlock().Text(strMessage);
});
}
}
迁移 winrt::resume_foreground (C++/WinRT)
如果在 C++/WinRT UWP 应用的协同程序中使用 winrt::resume_foreground 函数,则本部分适用。
在 UWP 中,winrt::resume_foreground 的用例是将执行切换到前台线程(前台线程通常是与 Windows.UI.Core.CoreDispatcher 相关联的线程)。 下面是该线程的一个示例。
// MainPage.cpp in a UWP app
winrt::fire_and_forget MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
...
co_await winrt::resume_foreground(this->Dispatcher());
...
}
在 Windows 应用 SDK 应用中:
- 需要使用 wil::resume_foreground(来自 Windows 实现库 (WIL)),而不是 winrt::resume_foreground。
- 并且需要使用 Microsoft.UI.Dispatching.DispatcherQueue 类,如将 Windows.UI.Core.CoreDispatcher 更改为 Microsoft.UI.Dispatching.DispatcherQueue 中所述,而不是使用 CoreDispatcher。
因此,请首先添加对 Microsoft.Windows.ImplementationLibrary NuGet 包的引用。
然后,将以下 include 添加到目标项目的 pch.h
中。
#include <wil/cppwinrt_helpers.h>
接下来,请遵循下面所示的模式。
// MainPage.xaml.cpp in a Windows App SDK app
...
winrt::fire_and_forget MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
...
co_await wil::resume_foreground(this->DispatcherQueue());
...
}