執行緒功能移轉
本主題介紹如何將通用 Windows 平台 (UWP) 應用程式中的執行緒程式碼移轉到 Windows 應用程式 SDK。
API 和/或功能差異摘要
UWP 的執行緒模型是單執行緒單元 (STA) 模型的變體,稱為應用程式 STA (ASTA),它阻止重入並有助於避免各種重入錯誤和死鎖。 ASTA 執行緒也稱為 UI 執行緒。
Windows 應用程式 SDK 使用標準 STA 執行緒模型,該模型不提供相同的重入保護措施。
CoreDispatcher 類型會移轉至 DispatcherQueue。 而 CoreDispatcher.RunAsync 方法會移轉至 DispatcherQueue.TryEnqueue。
C++/WinRT。 如果您要搭配 CoreDispatcher 使用 winrt::resume_foreground。
ASTA 到 STA 執行緒模型
如需 ASTA 執行緒模型的詳細資訊,請參閱部落格文章關於應用程式 STA 有何特殊?。
由於 Windows 應用程式 SDK 的 STA 執行緒模型在防止重新進入問題方面沒有相同的保證,如果您的 UWP 應用程式假設 ASTA 執行緒模型的非重新進入行為,則您的程式代碼可能無法如預期般運作。
要注意的一件事是重新進入 XAML 控制項 (請參閱 UWP 相片編輯器範例應用程式 (C++/WinRT) Windows 應用程式 SDK 移轉中的範例)。 而對於某些損毀,例如存取違規,直接損毀呼叫堆棧通常是要使用的正確堆棧。 但如果這是一個隱藏的異常崩潰 (異常碼:0xc000027b),那麼需要做更多的工作才能獲得正確的呼叫堆疊。
存放異常
隱藏的異常崩潰可以避免可能的錯誤,如果程式碼沒有任何部分處理異常,則稍後會使用該錯誤。 XAML 有時會立即確定錯誤是致命的,在這種情況下,直接崩潰堆疊可能會很好。 但更常見的是,堆疊在被確定為致命之前就已經展開。 如需有關存根例外狀況的詳細資訊,請參閱內部顯示情節 Stowed Exception C000027B。
對於儲存的異常崩潰 (以查看嵌套訊息泵,或查看拋出的XAML 控制項的特定異常),您可以透過在Windows 偵錯器 (WinDbg) 中載入崩潰轉儲來取得有關崩潰的更多資訊 (請請參閱下載 Windows 偵錯工具),然後使用!pde.dse
轉儲儲存的異常。
透過從 OneDrive 下載 PDE*.zip!pde.dse
檔案可以獲得 PDE 偵錯器擴充功能 (針對該命令)。 將適當的 x64 或 x86 .dll
從該 zip 檔案放入 winext
WinDbg 安裝目錄,然後 !pde.dse
處理存根例外狀況損毀傾印。
經常會有多個存根例外狀況,其中有些在結尾已處理/忽略。 最常見的是,第一個存根例外狀況是有趣的例外狀況。 在某些情況下,第一個存根例外狀況可能是第二個重新擲回的例外狀況,因此,如果第二個存根例外狀況顯示與第一個堆疊更深,則第二個例外狀況可能是錯誤的原點。 每個存根例外狀況所顯示的錯誤碼也很重要,因為該錯誤碼會提供與該例外狀況相關聯的 HRESULT。
將 Windows.UI.Core.CoreDispatcher 變更為 Microsoft.UI.Dispatching.DispatcherQueue
如果您在 UWP app 中使用 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 的 DispatcherQueue 時,您將呼叫 DependencyObject.DispatcherQueue (大部分的 XAML 物件都是 DependencyObject)。
// 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) 方法。 它會將工作新增至 Microsoft.UI.Dispatching.DispatcherQueue,該工作將在與 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 類別,而不是 CoreDispatcher,如將 Windows.UI.Core.CoreDispatcher 變更為 Microsoft.UI.Dispatching.DispatcherQueue 中所述。
因此,請先新增 Microsoft.Windows.ImplementationLibrary NuGet 套件的參考。
然後將以下包含新增到目標項目中的 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());
...
}