【问题标题】:Is it OK to check if (this == NULL) in C++ in order to terminate a detached thread's execution?是否可以在 C++ 中检查 (this == NULL) 以终止分离线程的执行?
【发布时间】:2014-10-30 19:19:15
【问题描述】:

假设我创建了一个类 X 的对象。当类 X 被构造时,它会在一个无限循环的函数上创建一个分离的线程。当 X 类被销毁时,我想终止(或中断)这个分离的线程。这可能吗?有没有更好的方法来实现这一目标?

例如:

class X
{
public:
    X(){
        startThread();
    }
    virtual ~X(){

    }

    void myThread()
    {
        cout << "start of myThread()" << endl;

        int loop = 0;
        while(true)
        {
            //std::this_thread::sleep_for(std::chrono::seconds(5));
            ++loop;
            cout << "loop # " << loop << endl;
            if(this == NULL){
                break;
            }
        }

        cout << "end of myThread()" << endl;
    }

    void startThread()
    {
        std::thread example(&X::myThread, this);
        example.detach();
    }
};

【问题讨论】:

  • 不,不行。当“this”为NULL时,“这个对象不存在。因为“this”是指向你所在对象的指针。除非对象不存在,否则它永远不会为NULL。这意味着对象已经被销毁通过析构函数。通过“不存在”,我的意思是你会得到 Seg 错误,或者它可能会工作,但稍后可能会导致 Seg 错误。因为这是指向内存中具有此对象的位置,但这是任何人都可以使用的空闲内存可以使用。
  • 呃,this 是一个地址,它不会因为你破坏了那个地址的东西而改变...
  • 啊,我明白了。在这个例子中,myThread 被分离后,似乎会无限运行(甚至在对象类 X 被销毁之后)。鉴于对象 X 已被销毁,是否有办法让线程 (myThread) 脱离其 while 循环?
  • @user1456962 在对象被销毁后让线程退出是不安全的,因为您可以在线程意识到对象被销毁之前访问不再存在的数据。您必须在对象被销毁之前使线程退出。您可能还可以摆脱类似的情况:确保在对象被销毁之前通知它并停止访问数据,然后在对象被销毁之后自行退出,但我不确定这是否安全线程是一个非静态成员函数。
  • @user1061392,感谢您的回复!这说得通。 this 永远不应该为 NULL,因为它毕竟只是一个地址。当它被销毁时,这将指向其他对象并稍后给出段错误。

标签: c++ multithreading thread-safety this destructor


【解决方案1】:

在线程还在运行的时候删除X对象肯定不行;由于仍在运行的线程试图访问现在已释放的数据,因此您最终会导致大量数据崩溃和损坏。

您的主线程(即产生 myThread 线程的线程)需要做的是:

  1. 不知何故告诉线程自愿停止循环并退出(例如,通过更改 myThread 定期检查的 std::atomic 的值,或在 myThread 正在接收的套接字上发送一个字节,或类似的东西)
  2. 在线程对象上调用 join()。 join() 将阻塞,直到线程完全退出。

一旦 join() 返回,您就可以安全地继续删除 X 对象。

(旁注:如果您在 X 类的析构函数中执行上述步骤,那么只要您从未子类化 X,或者(如果您使用子类 X)线程永远不会访问来自子类(例如,如果线程从不调用任何虚拟方法)。这是因为在调用子类析构函数(内部线程仍在运行时)和调用 X 析构函数(停止线程)之间会有竞争条件) 在此期间线程可以从子类的层访问现在已释放的数据)

【讨论】:

  • 您可能会补充说,this == NULL 不可能在给定的代码中为真。这有两个原因: 1. this 通常在函数中实现为局部变量,因此任何合法操作都无法从另一个线程修改它。 2. 标准规定如果this 为空指针则为未定义行为,因此编译器可能会完全优化掉if() {} 语句。
  • @Jeremy,感谢您提供此信息!我可以简单地在某处创建一个静态布尔变量并让线程根据该变量的值自愿停止循环/退出吗? (我对“volatile boolean”和“atomic”值不太熟悉)。这是原子值的设计目的吗? en.cppreference.com/w/cpp/atomic/atomic
  • “例如,通过设置一个 volatile 布尔值”——请不要延续 volatile 变量与并发性有关的神话!在标准 c++ 中执行此操作的唯一正确方法是使用 std::atomic&lt;bool&gt;!
  • @user1456962 原子值经过特殊处理,使得它们可以被多个线程访问而无需任何序列化,并且仍然给出有意义的结果。 (特殊处理会降低访问它们的效率,但如果没有它,您可能会在某些硬件上出现意外行为;例如,线程 B 永远不会“看到”线程 A 所做的更新)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-06
  • 2019-03-15
  • 2021-06-08
  • 2015-06-02
  • 2012-06-29
  • 2011-11-01
  • 2018-06-28
相关资源
最近更新 更多