假设我有一个ViewModel,当用户离开其绑定的View时可以销毁它。析构函数对订阅成员变量执行清理:
MyViewModel::~MyViewModel()
{
if (m_subscription)
{
if (m_contentChangedToken.Value != 0)
{
m_subscription->ContentChanged -= m_contentChangedToken;
m_contentChangedToken.Value = 0;
}
}
}
创建ViewModel后,将运行一个异步获取订阅的函数,将其分配给成员变量并分配事件侦听器
void MyViewModel::AwesomeFunctionAsync()
{
create_task(TargetedContentSubscription::GetAsync(c_subId))
.then([this](TargetedContentSubscription^ subscription)
{
if (subscription)
{
m_subscription = subscription;
m_contentChangedToken = m_subscription->ContentChanged += // attach event
}
}, task_continuation_context::use_arbitrary());
}
现在假设我的ViewModel正在被销毁,而后台线程正在AwesomeFunctionAsync中运行代码。潜伏在这里的竞争条件是什么?例如,析构函数可能在后台线程附加事件之前运行吗?或者我可以相信析构函数总是因GC而持续吗?
答案 0 :(得分:1)
除非有人明确地尝试delete
对象,否则你会没事,因为lambda捕获this
指针并保持活着状态。
例如,尝试以下简单测试:
ref struct TestClass sealed
{
void DoStuffAsync()
{
concurrency::create_async([this]()
{
Sleep(1000);
PrintValue();
});
}
void PrintValue()
{
// Accessing 'name' after deletion is undefined behavior, but it
// "works on my machine" for the purposes of this demonstration.
std::string message = name + ": PrintValue is running.";
// Accessing 'data.size()' after deletion is also undefined behavior
if (data.size() == 0)
{
message += " Oops, I'm about to crash\r\n";
}
else
{
message = message + " Data is " + std::to_string(data[0]) +
", " + std::to_string(data[1]) + "\r\n";
}
OutputDebugStringA(message.c_str());
}
virtual ~TestClass()
{
std::string message = name + ": Destructor is running.\r\n";
OutputDebugStringA(message.c_str());
}
internal: // so we can use 'const char *'
TestClass(const char* name) : name{ name }, data{ 1, 2 }
{
std::string message = this->name + ": Constructor is running.\r\n";
OutputDebugStringA(message.c_str());
}
private:
std::string name;
std::vector<int> data;
};
void Test()
{
OutputDebugStringA("Starting 'no async' test\r\n");
{
auto c = ref new TestClass("no async");
c->PrintValue();
}
OutputDebugStringA("---\r\nDone. Starting 'async' test\r\n");
{
auto c = ref new TestClass("async");
c->DoStuffAsync();
}
OutputDebugStringA("---\r\nDone. Starting 'explicit delete' test\r\n");
{
auto c = ref new TestClass("explicit delete");
c->DoStuffAsync();
delete c;
}
}
MainPage::MainPage()
{
InitializeComponent();
Test();
}
运行它时,您会在“输出”窗口中看到类似的内容:
Starting 'no async' test
no async: Constructor is running.
no async: PrintValue is running. Data is 1, 2
no async: Destructor is running.
--- Done. Starting 'async' test
async: Constructor is running.
--- Done. Starting 'explicit delete' test
explicit delete: Constructor is running.
explicit delete: Destructor is running.
async: PrintValue is running. Data is 1, 2
: PrintValue is running. Oops, I'm about to crash
async: Destructor is running.
注意'async'
版本在PrintValue
异步运行之后才运行析构函数。但是'explicit delete'
版本会破坏该对象,该对象在大约1秒后尝试访问成员变量时会崩溃。 (您可以看到访问name
不会崩溃 - 尽管它是未定义的行为 - 但如果您尝试访问data
的元素,您将获得异常(或更糟))。