weak_ptr 中的可能错误已传递给线程

Probable bug in weak_ptr passed to a thread

在此代码示例中,我们将 std::shared_ptr<int> 传递给线程,并期望它被削弱为 std::weak_ptr<int>。但在 Visual Studio 2019 年,t 具有很强的参考性,并且该程序的输出在调试和发布版本中都是“pw alive”。 linux 与编译器 g++ -lpthread.

的相同问题

这个玩具示例的动机是更严重的情况,即线程 t 进行一些长时间的计算,然后通过共享资源发送结果 t 作为参数接收。这些资源可能在 t 的计算期间被其他线程删除,在这种情况下 t 变得无用并且可以提前终止。但是由于这个(疑似)错误,t 使资源保持活动状态并浪费内存和计算时间。

这真的是一个错误,还是我误解了 weak_ptrshared_ptr 之间的关系?如果这是一个错误,您知道在哪里报告吗? Visual Studio 和 g++ 可能在不同的地方。

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

void TestWeakPtrAlive(std::weak_ptr<int> pw)
{
  // wait for main thread to delete pw
  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  std::shared_ptr<int> p = pw.lock();
  std::cout << (p ? "pw alive" : "pw dead")
            << std::endl;
}

int main()
{
  std::shared_ptr<int> p = std::make_shared<int>(18);
  std::thread t(TestWeakPtrAlive, p);
  p = nullptr;
  t.join();
  return 0;
}

这些实现复制了 shared_ptr。即他们复制参数并将其绑定到线程的生命周期。复制是线程创建参数绑定的默认设置。

这不会被视为错误,除非标准规定实现不能那样做。

就获得您想要的行为而言,我认为如果您使用 lambda 也会遇到同样的问题。相反,您可以使用函子,或者您可以使用一些全局或静态变量。

这是转换为使用仿函数的示例。好处是您可以控制成员变量。缺点是样板。

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

class TestWeakPtrAlive
{
    std::weak_ptr<int> m_pw;

public:
    TestWeakPtrAlive(std::weak_ptr<int> pw)
        : m_pw(std::move(pw))
    {}
  
    void operator()()
    {
        // wait for main thread to delete pw
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::shared_ptr<int> p = m_pw.lock();
        std::cout << (p ? "pw alive" : "pw dead")
                  << std::endl;
    }
};

int main()
{
    std::shared_ptr<int> p = std::make_shared<int>(18);
    auto functor = TestWeakPtrAlive(p);
    std::thread t(std::move(functor));
    p = nullptr;
    t.join();
    return 0;
}

编辑: 我刚刚想到了一个相当明显的解决方案,即创建一个 weak_ptr 并将其传递给线程构造函数。

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

void TestWeakPtrAlive(std::weak_ptr<int> pw)
{
    // wait for main thread to delete pw
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    std::shared_ptr<int> p = pw.lock();
    std::cout << (p ? "pw alive" : "pw dead")
        << std::endl;
}

int main()
{
    std::shared_ptr<int> p = std::make_shared<int>(18);
    std::weak_ptr<int> wp = p;
    std::thread t(TestWeakPtrAlive, std::move(wp));
    p = nullptr;
    t.join();
    return 0;
}