在使用共享指针调用的函数中使用 this 是有效的
Is valid to use this in a function that is called using shared pointer
我对 C++ 中共享指针的使用和多线程编程有疑问。
如果我在线程 A 中使用 this class 的共享指针对象调用成员函数,
然后在 class 中使用 this 属性并将其传递到将从线程 B 触发的回调函数中是有效的。
从我的角度来看,如果线程 A 完成其工作,则共享指针将过期并且这将无效。
使用 shared_from_this 并将其转换为 void* 是否有效?
你好,这是代码。这段代码会产生段错误,对吧?
#include <memory>
#include <iostream>
#include <thread>
#include <unistd.h>
#include "ClassB.h"
class B;
class A
{
public:
A()= default;
void foo(void * ptr)
{
std::cout <<"enter in foo" <<std::endl;
sleep(1);
std::cout << "Thread wake up" << std::endl;
B *pB = reinterpret_cast<B*>(ptr);
pB->x = 5;
}
};
void B::bar()
{
std::cout << "enter in bar" << std::endl;
void * ptr = reinterpret_cast<void *>(this);
auto t2= std::thread(&A::foo,A(),ptr);
t2.detach();
}
void threadTask()
{
std::cout << "ThreadTask" << std::endl;
std::shared_ptr<B> psharedB = std::make_shared<B>();
auto t1 = std::thread(&B::bar,psharedB);
t1.detach();
}
int main()
{
auto t = std::thread(threadTask);
t.join();
sleep(20);
return 0;
}
谢谢
乔治
如果对象是在堆上分配的(使用new
),它不会在线程结束时变为无效。 shared_pointer
存储了一个指向堆上实例的指针和一个指向 count 变量的指针。如果 shared_pointer
被复制,计数变量会增加,如果它的析构函数被调用,它会减少。如果计数为 0,则对象的析构函数将由共享指针调用,因此如果没有 shared_pointer
s 指向该对象。 shared_pointer
应该是线程保存,即使这是非常耗费资源的。重要的是要知道,如果您将指针传递给 shared_pointer
,则 shared_pointer
现在负责管理该实例,但它不会影响该指针的任何其他用途。因此,您不应在指向该对象的其他指针上调用 delete。
因此,如果您使用 new
.
分配对象,则不会引起任何冲突
编辑:
这不应该工作,因为 shared_pointer
将随着线程分离和范围离开而被销毁,并且 count=1
B
应该被销毁。
分离线程总是一个坏主意。
std::shared_ptr<B> psharedB = std::make_shared<B>();
auto t1 = std::thread(&B::bar,psharedB);
我不是语言律师,但我认为上面的代码将创建共享指针 psharedB
指向的 class B
的实例,然后创建一个实例std::thread
按值 传递 一个指向方法B::bar
的函数指针作为第一个参数,并按值 传递一个copy of psharedB
作为第二个参数。
因为 psharedB
的副本被传递给 thread ( … )
构造函数,共享指针的引用计数增加到两个。您可以通过将以下代码添加到 threadTask
来探索这一点,以在构建新线程后检查 psharedB.use_count()
的值……
auto t1 = std::thread(&B::bar, psharedB);
// show use count is two not one
std::cout << "psharedB use count: " << psharedB.use_count() << std::endl;
…文档警告说 use_count 返回的值在多线程环境中是 "approximate" 但在这种特殊情况下它应该是准确的。
当 threadtask
退出时,psharedB
超出范围。共享指针的使用计数被共享指针的析构函数减少,但是 B
对象实例保持活动状态,因为传递给 t1
线程的 psharedB
副本仍然存在,所以使用计数是一个不为零。
在 B::bar
中,变量 this
是一个普通的原始 B *
指针。在幕后,它是从传递给 t1
线程的智能指针中提取的,但就 B::bar
中的逻辑而言,我们并不知道。因此,当该指针被强制转换为 void *
时,它根本不会影响共享指针的使用计数。
探索这一点的一种方法是在 B::bar
和 运行 中的 reinterpret_cast
上放置一个断点。紧接在断点下方的调用堆栈中的条目将是使用 psharedB
的副本调用 B::bar
的逻辑。调试器应该允许您查看共享指针并检查其使用计数,如果 threadTask
已经退出则为 1,如果 threadTask
尚未退出则为 2。
跨过 reinterpret_cast
并观察共享指针的使用计数没有改变。
当 B::bar
returns 时,threadTask
启动的 t1
线程终止。实际上,传递给该线程的 psharedB
副本超出范围并被破坏。共享指针使用计数变为零,最初由 threadTask
创建的 B
对象实例也被销毁。
此时传递给foo
的ptr
值是指向已删除对象实例的指针。任何在 foo
中取消引用 ptr
值的尝试都将导致未定义的行为。如果你幸运的话,它会使你的程序崩溃。如果你不走运,取消引用似乎起作用了,你会在程序执行的后期难以追踪失败。或者一些奇怪的行为,只是偶尔发生并且很难重现。
更糟糕的是,根据 when,t1
线程被销毁,因此 when B
对象实例被销毁,foo
中的变量 ptr
对于 foo
的某些执行可能是有效的 B*
指针,对于 [=47] 的其余部分可能是无效指针=]的执行。
Is valid to use the shared_from_this and cast this to void*?
您的 A
和 B
class 不是从 std::enable_shared_from_this<T>
派生的,因此在您的示例中,在任一 class。
如果您确实 创建了一个shared_ptr<B>
实例,您会发现无法将其转换为void *
。尝试将 auto temp = reinterpret_cast<void *> (psharedB);
添加到 threadTask
。您会发现编译器错误,因为转换是无效转换。
Is it valid to use this in a function that is called using shared pointer
是的,它是有效的 - 在一定的限制范围内。从 threadTask
调用的方法 B::bar
是一个使用共享指针调用的函数。您可以在 B::bar
中使用 this
。但是您不会因为 this
是使用共享指针调用它而获得任何特殊功能。就 B::bar
而言,它可以使用这样的原始指针调用......
B* p = new B();
p->bar();
无论您使用的是共享指针还是原始指针 - 将 this
或 this
的 void *
版本传递给另一个函数(例如 [=74)是无效的=] 没有确保指针指向的实例在 A::foo
取消引用该指针时仍然有效。
如果您使用 shared_ptr<T>
来控制对象的生命周期,那么理想情况下您希望传递 A::foo
一个 shared_ptr<B>
参数。这样,即使所有其他共享指针都被销毁,对象也会通过传递给 A::foo
的 shared_ptr
保持活动状态。
我对 C++ 中共享指针的使用和多线程编程有疑问。 如果我在线程 A 中使用 this class 的共享指针对象调用成员函数, 然后在 class 中使用 this 属性并将其传递到将从线程 B 触发的回调函数中是有效的。 从我的角度来看,如果线程 A 完成其工作,则共享指针将过期并且这将无效。
使用 shared_from_this 并将其转换为 void* 是否有效?
你好,这是代码。这段代码会产生段错误,对吧?
#include <memory>
#include <iostream>
#include <thread>
#include <unistd.h>
#include "ClassB.h"
class B;
class A
{
public:
A()= default;
void foo(void * ptr)
{
std::cout <<"enter in foo" <<std::endl;
sleep(1);
std::cout << "Thread wake up" << std::endl;
B *pB = reinterpret_cast<B*>(ptr);
pB->x = 5;
}
};
void B::bar()
{
std::cout << "enter in bar" << std::endl;
void * ptr = reinterpret_cast<void *>(this);
auto t2= std::thread(&A::foo,A(),ptr);
t2.detach();
}
void threadTask()
{
std::cout << "ThreadTask" << std::endl;
std::shared_ptr<B> psharedB = std::make_shared<B>();
auto t1 = std::thread(&B::bar,psharedB);
t1.detach();
}
int main()
{
auto t = std::thread(threadTask);
t.join();
sleep(20);
return 0;
}
谢谢 乔治
如果对象是在堆上分配的(使用new
),它不会在线程结束时变为无效。 shared_pointer
存储了一个指向堆上实例的指针和一个指向 count 变量的指针。如果 shared_pointer
被复制,计数变量会增加,如果它的析构函数被调用,它会减少。如果计数为 0,则对象的析构函数将由共享指针调用,因此如果没有 shared_pointer
s 指向该对象。 shared_pointer
应该是线程保存,即使这是非常耗费资源的。重要的是要知道,如果您将指针传递给 shared_pointer
,则 shared_pointer
现在负责管理该实例,但它不会影响该指针的任何其他用途。因此,您不应在指向该对象的其他指针上调用 delete。
因此,如果您使用 new
.
编辑:
这不应该工作,因为 shared_pointer
将随着线程分离和范围离开而被销毁,并且 count=1
B
应该被销毁。
分离线程总是一个坏主意。
std::shared_ptr<B> psharedB = std::make_shared<B>(); auto t1 = std::thread(&B::bar,psharedB);
我不是语言律师,但我认为上面的代码将创建共享指针 psharedB
指向的 class B
的实例,然后创建一个实例std::thread
按值 传递 一个指向方法B::bar
的函数指针作为第一个参数,并按值 传递一个copy of psharedB
作为第二个参数。
因为 psharedB
的副本被传递给 thread ( … )
构造函数,共享指针的引用计数增加到两个。您可以通过将以下代码添加到 threadTask
来探索这一点,以在构建新线程后检查 psharedB.use_count()
的值……
auto t1 = std::thread(&B::bar, psharedB);
// show use count is two not one
std::cout << "psharedB use count: " << psharedB.use_count() << std::endl;
…文档警告说 use_count 返回的值在多线程环境中是 "approximate" 但在这种特殊情况下它应该是准确的。
当 threadtask
退出时,psharedB
超出范围。共享指针的使用计数被共享指针的析构函数减少,但是 B
对象实例保持活动状态,因为传递给 t1
线程的 psharedB
副本仍然存在,所以使用计数是一个不为零。
在 B::bar
中,变量 this
是一个普通的原始 B *
指针。在幕后,它是从传递给 t1
线程的智能指针中提取的,但就 B::bar
中的逻辑而言,我们并不知道。因此,当该指针被强制转换为 void *
时,它根本不会影响共享指针的使用计数。
探索这一点的一种方法是在 B::bar
和 运行 中的 reinterpret_cast
上放置一个断点。紧接在断点下方的调用堆栈中的条目将是使用 psharedB
的副本调用 B::bar
的逻辑。调试器应该允许您查看共享指针并检查其使用计数,如果 threadTask
已经退出则为 1,如果 threadTask
尚未退出则为 2。
跨过 reinterpret_cast
并观察共享指针的使用计数没有改变。
当 B::bar
returns 时,threadTask
启动的 t1
线程终止。实际上,传递给该线程的 psharedB
副本超出范围并被破坏。共享指针使用计数变为零,最初由 threadTask
创建的 B
对象实例也被销毁。
此时传递给foo
的ptr
值是指向已删除对象实例的指针。任何在 foo
中取消引用 ptr
值的尝试都将导致未定义的行为。如果你幸运的话,它会使你的程序崩溃。如果你不走运,取消引用似乎起作用了,你会在程序执行的后期难以追踪失败。或者一些奇怪的行为,只是偶尔发生并且很难重现。
更糟糕的是,根据 when,t1
线程被销毁,因此 when B
对象实例被销毁,foo
中的变量 ptr
对于 foo
的某些执行可能是有效的 B*
指针,对于 [=47] 的其余部分可能是无效指针=]的执行。
Is valid to use the shared_from_this and cast this to void*?
您的 A
和 B
class 不是从 std::enable_shared_from_this<T>
派生的,因此在您的示例中,在任一 class。
如果您确实 创建了一个shared_ptr<B>
实例,您会发现无法将其转换为void *
。尝试将 auto temp = reinterpret_cast<void *> (psharedB);
添加到 threadTask
。您会发现编译器错误,因为转换是无效转换。
Is it valid to use this in a function that is called using shared pointer
是的,它是有效的 - 在一定的限制范围内。从 threadTask
调用的方法 B::bar
是一个使用共享指针调用的函数。您可以在 B::bar
中使用 this
。但是您不会因为 this
是使用共享指针调用它而获得任何特殊功能。就 B::bar
而言,它可以使用这样的原始指针调用......
B* p = new B();
p->bar();
无论您使用的是共享指针还是原始指针 - 将 this
或 this
的 void *
版本传递给另一个函数(例如 [=74)是无效的=] 没有确保指针指向的实例在 A::foo
取消引用该指针时仍然有效。
如果您使用 shared_ptr<T>
来控制对象的生命周期,那么理想情况下您希望传递 A::foo
一个 shared_ptr<B>
参数。这样,即使所有其他共享指针都被销毁,对象也会通过传递给 A::foo
的 shared_ptr
保持活动状态。