C++/Winrt学习笔记
Concurrency and asynchronous operations with C++/WinRT
超过50毫秒的操作,WinRT一般都会将其设为异步操作。异步操作意味着操作调用者不必阻塞并等待操作完成(但是可以选择阻塞)。
WinRT定义了四种基本的异步操作
- IAsyncAction,
- IAsyncActionWithProgress,
- IAsyncOperation, and
- IAsyncOperationWithProgress<TResult, TProgress>.
IAsyncOperation和IAsyncOperationWithProgress<TResult, TProgress>中的类型参数只支持使用WinRT类型,否则会出现must be WinRT type"
错误。
如果想要使用非WinRT类型,那么默认不支持从routine,但是可以使用传统的回调方式,比如concurrency::task(比std::future性能高一些)。
Coroutine的入口最好传WinRT类型的值,传值能够增加到WinRT类型实例的引用,保证该值的生命周期。传引用的话,当coroutine挂起的时候,其他执行过程有可能释放该值的实例,导致coroutine中该值引用无效的实例。
如果非要传引用,一个变通的办法是在挂起之前对传入的引用指向的值进行拷贝。
Standard arrays and vectors有说明如何传递一个标准的容器到异步函数中。
如果coroutine是类成员,那么需要保证this指针不被释放,参考 Strong and weak references in C++/WinRT
Strong and weak references in C++/WinRT
WinRT是基于COM,然后两者都是通过引用计数的方式来管理实例。采用引用计数的话,就有所有权问题。谁拥有强引用,就代表谁对对象实例拥有所有权;谁拥有弱引用,则表示谁只有使用权。强引用和弱引用的区别是,弱引用有可能会变得无效,因为对象被释放了。
如果一个类的成员函数是异步的,那么这个成员在执行的时候隐式地保留了类对象的指针,也就是this指针。万一,在异步执行的过程中,类对象被释放,那么this指针就指向了无效的区域。为了避免这个问题, winrt::implements提供了get_strong()
函数,让类成员函数可以获取类对象,也就是this指针的强引用,避免对象被提前释放:
IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
auto strong_this{ get_strong() }; // Keep *this* alive.
co_await 5s;
co_return m_value;
}
强引用会阻止对象被释放,有时候这会成为一个问题。如果不想组织对象被释放,只是想检查一下对象是否可以用,则可以使用弱引用:implements::get_weak
:
IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
auto weak_this{ get_weak() }; // Maybe keep *this* alive.
co_await 5s;
if (auto strong_this{ weak_this.get() })
{
co_return m_value;
}
else
{
co_return L"";
}
}
另一个常见的跟生命周期相关的问题发生在事件处理器中。如果一个类的成员函数被注册为一个事件的处理器,那么就得注意在这个类对象生命周期结束时取消注册事件处理。但是有时候即便注册取消了,依然会有事件进来,导致之前注册的成员函数被调用,在对象销毁的情况下,这大概率会导致灾难,特别是当该成员函数访问了类的内部状态。
解决办法是使用以强引用或者弱引用为参数的lambda作为事件处理回调,而不是隐式传递this:
event_source.Event([this, strong_this { get_strong()}](auto&& ...)
{
std::wcout << m_value.c_str() << std::endl;
});
或者
event_source.Event([strong_this { get_strong()}](auto&& ...)
{
std::wcout << strong_this->m_value.c_str() << std::endl;
});
弱引用的例子:
event_source.Event([weak_this{ get_weak() }](auto&& ...)
{
if (auto strong_this{ weak_this.get() })
{
std::wcout << strong_this->m_value.c_str() << std::endl;
}
});
不使用lambda,直接注册成员函数为事件处理器:
event_source.Event({ get_strong(), &EventRecipient::OnEvent });
一个使用弱引用的例子:
winrt::Windows::UI::Xaml::Controls::SwapChainPanel m_swapChainPanel;
winrt::event_token m_compositionScaleChangedEventToken;
void RegisterEventHandler()
{
m_compositionScaleChangedEventToken = m_swapChainPanel.CompositionScaleChanged([weak_this{ get_weak() }]
(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
Windows::Foundation::IInspectable const& object)
{
if (auto strong_this{ weak_this.get() })
{
strong_this->OnCompositionScaleChanged(sender, object);
}
});
}
void OnCompositionScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
Windows::Foundation::IInspectable const& object)
{
//
}
C++/WinRT通过winrt::implements
默认为winrt类型提供弱引用支持,在IWeakReferenceSource被查询的时候启用。
例子:
Class c;
winrt::weak_ref<Class> weak{ c };
auto weak = winrt::make_weak(c);
可以选择关闭这项功能:
struct MyImplementation: implements<MyImplementation, IStringable, no_weak_ref>
{
...
}
或者
struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, no_weak_ref>
{
...
}
(本篇完)