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_ptr
和 shared_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;
}
在此代码示例中,我们将 std::shared_ptr<int>
传递给线程,并期望它被削弱为 std::weak_ptr<int>
。但在 Visual Studio 2019 年,t
具有很强的参考性,并且该程序的输出在调试和发布版本中都是“pw alive”。 linux 与编译器 g++ -lpthread
.
这个玩具示例的动机是更严重的情况,即线程 t
进行一些长时间的计算,然后通过共享资源发送结果 t
作为参数接收。这些资源可能在 t
的计算期间被其他线程删除,在这种情况下 t
变得无用并且可以提前终止。但是由于这个(疑似)错误,t
使资源保持活动状态并浪费内存和计算时间。
这真的是一个错误,还是我误解了 weak_ptr
和 shared_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;
}