【发布时间】:2018-04-10 20:55:31
【问题描述】:
我有一个带有 C++ 和 Visual Studio 2017 社区版的示例 UWP 应用,我正在努力了解 PPL 功能。
我生成了一个 UWP 应用,然后对 MainPage.xaml.cpp 文件进行了以下修改。这些更改的目的是模拟一个耗时数秒的异步操作,并在操作完成各个阶段时更新显示的 UI。
这可行,并且 UI 已更新。
但是我在编译时确实看到了以下警告。
1> ... \appuwp1\mainpage.xaml.cpp(46): warning C4451: 'AppUwp1::MainPage::{ctor}::<lambda_df2e69e2b6fe4b1dfba3f26ad0398a3e>::myThread': Usage of ref class 'Windows::UI::Core::CoreWindow' inside this context can lead to invalid marshaling of object across contexts
1> ... \appuwp1\mainpage.xaml.cpp(46): note: Consider using 'Platform::Agile<Windows::UI::Core::CoreWindow>' instead
1> ... \appuwp1\mainpage.xaml.cpp(56): warning C4451: 'AppUwp1::MainPage::{ctor}::<lambda_c1468d2f6468239bd456bea931167a21>::myThread': Usage of ref class 'Windows::UI::Core::CoreWindow' inside this context can lead to invalid marshaling of object across contexts
1> ... \appuwp1\mainpage.xaml.cpp(56): note: Consider using 'Platform::Agile<Windows::UI::Core::CoreWindow>' instead
这些警告是什么意思?
我确实找到了Threading and Marshaling (C++/CX) 的解释,其中提到了警告“使用非敏捷类 (C4451) 时的编译器警告”,但是我不确定我是否有实际问题。
是否有不同的、更可接受的方式从任务延续更新 UI?
我正在使用DispatchedHandler() 以便从任务继续访问 UI 线程。如果我尝试使用 myTextBlock->Text = "this is my text and some more text after sleep"; 而不将其包装在 DispatchedHandler() 中,则会出现异常。该异常是可以理解的,因为 then 任务延续不再在 UI 线程中运行。
这个stackoverflow,Warning C4451: Usage of ref class BackgroundTaskDeferral can lead to invalid marshaling 表示使用Platform:Agile 确实解决了他们的警告。
但是没有解释警告的实际含义
初始任务创建除了启动处理异步操作的线程之外什么都不做。每个then 延续子句都执行Sleep() 来表示一些需要时间的操作,然后用消息更新显示的UI 屏幕。
MainPage::MainPage()
{
InitializeComponent();
myTextBlock->Text = "this is my text and some more text";
auto myThread = CoreWindow::GetForCurrentThread();
concurrency::create_task ([=]() {
// we are wanting to spin off a task that will be
// performed asynchronously and the real work is done in the
// following task continuations.
Sleep(5000);
}).then([=]()
{
Sleep(5000);
myThread->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler([=]()
{
// Do stuff on the UI Thread
myTextBlock->Text = "this is my text and some more text after sleep";
}));
}).then([=]() // warning C4451 for this line
{
Sleep(5000);
myThread->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler([=]()
{
// Do stuff on the UI Thread
myTextBlock->Text = "this is my text and some more text after sleep after sleep again";
}));
}); // warning C4451 for this line
}
额外探索#1
随着MainPage::MainPage() 的以下更改,我看到 UI 窗口中显示了预期的一系列消息。在几秒钟的过程中显示了一系列文本字符串,包括一系列以iCount的递增值开头的字符串,这些字符串在第一个任务继续的循环中生成。
似乎如果for (int iCount = 0; iCount < 3; iCount++) { 被放置在new DispatchedHandler() lambda 中,它会导致 UI 线程阻塞数秒并且 UI 变得无响应,然后显示第二个任务继续的文本字符串,并且UI 再次变得响应。如果for 在此源代码示例中位于外部,则 UI 线程不会被阻塞并且 UI 保持响应。
这是否意味着 new DispatchedHandler() 中包含的 lambda 被交给 UI 线程运行?
MainPage::MainPage()
{
InitializeComponent();
myTextBlock->Text = "this is my text and some more text";
auto myThread = CoreWindow::GetForCurrentThread();
concurrency::create_task ([=]() {
Sleep(2000);
myThread->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler([=]()
{
myTextBlock->Text = "start of task";
// Do stuff on the UI Thread
}));
}).then([=]()
{
Sleep(5000);
for (int iCount = 0; iCount < 3; iCount++) {
myThread->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler([=]()
{
// Do stuff on the UI Thread
std::wstringstream ss;
ss << iCount << " text first";
myTextBlock->Text = ref new Platform::String(ss.str().c_str());
} ) // close off the DispatchedHandler() lambda
); // close off the RunAsync()
Sleep(2000);
} // close off for loop
}).then([=]()
{
Sleep(5000);
myThread->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler([=]()
{
// Do stuff on the UI Thread
myTextBlock->Text = "this is my text and some more text after sleep after sleep again";
}));
});
}
补充说明
MVVM and Accessing the UI Thread in Windows Store Apps
Running WPF Application with Multiple UI Threads
另请参阅其他 stackoverflow 帖子:
- Run code on UI thread in WinRT
- How do I determine if I need to dispatch to UI thread in WinRT/Metro?
MSDN article: Concurrency Runtime
Task Parallelism (Concurrency Runtime) 提供并发运行时和各种选项的概述。几个示例和大量指向其他材料的链接。
【问题讨论】:
-
您的第一个评论块不正确 - 它在线程池线程上运行。此外,您不应该在函数的开头使用
Sleep(2000),因为它确实在 UI 线程上。编译器警告您,如果您从错误的线程中使用CoreWindow,它将失败。Dispatcher用法可确保您不会那样做。 -
@PeterTorr-MSFT 对不起,但我真的不觉得你的评论对我的实际问题很有帮助。我对评论块进行了更改并移动了
Sleep(),但是我仍然想要一些资源,其中包含有关从另一个线程访问 UI 的更好信息。 -
我的最后两句话解释了您收到警告的原因以及您的代码为何有效。
Dispatcher->RunAsync调用确保工作在适当的线程上完成。您可以阅读有关Marshalling on Wikipedia 或COM threading 的更多信息。 -
第二个示例中的不同之处在于您将
CoreDispatcher(敏捷)传递给 lambda,但在第一个示例中,您传递了整个CoreWindow(这不是敏捷)。 -
@PeterTorr-MSFT 感谢您的确认。我认为可能是这种情况,并且打算对敏捷和非敏捷做一些额外的研究。为了回答这个发布的问题stackoverflow.com/questions/49642684/…,我在研究中得到了一些关注,这似乎有点相关。与其说是问题,不如说是专门针对 Task 的研究。
标签: visual-c++ uwp task-parallel-library