使用 PPL 时是否需要将我的析构函数与异步函数同步?

Do I need to synchronize my destructor with async functions when using PPL?

假设我有一个 ViewModel,当用户离开其绑定的 View 时可以将其销毁。析构函数对订阅成员变量执行清理:

MyViewModel::~MyViewModel()
{
    if (m_subscription)
    {
        if (m_contentChangedToken.Value != 0)
        {
            m_subscription->ContentChanged -= m_contentChangedToken;
            m_contentChangedToken.Value = 0;
        }
    }
}

创建 ViewModel 后,异步获取订阅的函数 运行s,将其分配给成员变量,并分配事件侦听器

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 的原因析构函数总是最后的吗?

除非有人明确尝试 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();
}

当你 运行 它时,你会在输出中看到类似这样的东西 window:

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 的元素,您将得到一个异常(或更糟))。