【问题标题】:Thread cancellation (pthread) & C++线程取消 (pthread) & C++
【发布时间】:2011-05-12 14:41:26
【问题描述】:

我正在用 C++ 为 Linux 编写 MT 程序,我想知道线程取消是如何执行的。

据我所知,当线程被取消时,清理函数在线程函数内部被调用并且线程函数被强制退出。这意味着两件事:

  1. 当线程被取消时,它仍然会调用线程函数内部创建的所有 C++ 对象的析构函数。
  2. 我可以将指向在线程函数中创建的对象的指针传递给清理函数。

我是对的吗,下面的代码工作得很好?


下面代码中还有一个问题,当线程在SECTION A的某处被取消时,second_thread_cleanup_function()会首先被调用,对吧?

class SomeObject
{
    public:
        ~SimpleObject (void); // <- free dynamically allocated memory

        void finalize (void);

        // ...
}

void first_thread_cleanup_function (void* argument)
{
    SomeObject* object (argument);

    object->finalize ();
}

void second_thread_cleanup_function (void* argument)
{
    // ... do something ...
}

void* thread_function (viod* argument)
{
    SomeObject object;

    pthread_cleanup_push (first_thread_cleanup_function, &object);

    // ... some code ...

    pthread_cleanup_push (second_thread_cleanup_function, NULL);
    // ... SECTION A ...
    pthread_cleanup_pop (0);

    // .. some code ...

    pthread_cleanup_pop (1);
}

【问题讨论】:

  • 如果使用 C++ 编码,请记住 RAII:考虑使用 C++0x std::thread 或 C++ boost::thread,甚至通过异常模仿优雅退出(请参阅 drdobbs.com/architecture-and-design/207100682 for更多信息)。
  • 我在考虑,但我必须用线程做一些具体的事情。我知道,我(大部分)正在重新发明轮子……

标签: c++ linux multithreading pthreads cancellation


【解决方案1】:

只有在清理方法中释放分配的对象时才会调用析构函数。否则,不行。

是的,您在 A 部分中的清理调用顺序正确。

【讨论】:

  • 但是'object'的析构函数还是会被调用?
  • @Goofy - 我假设你的意思是object in thread_function。答案是不。 thread_function 将在您调用取消后的第一个取消点停止。线程的堆栈没有展开,唯一的清理发生在清理函数中。
  • 是的,我的意思是“线程函数”中的“对象”。要清楚 - 如果线程正常退出,它会展开堆栈并调用对象的析构函数?
  • 如果线程正常存在,那么一切都会像正常的函数调用一样结束。所以,是的。
  • @Donnie 似乎本地对象的析构函数在其线程被取消时被调用。看我的回答。
【解决方案2】:

对于任何使用 NPTL 的现代 Linux 发行版(实际上这意味着任何运行 2.6 内核的发行版),NPTL 将调用析构函数并以伪异常展开堆栈。

事实上,NPTL 坚持它,通过实现它所谓的强制堆栈展开。您可以使用 catch(...) 捕获伪异常,但如果这样做,您必须随后重新抛出它,否则整个过程将被终止。

克里斯

【讨论】:

    【解决方案3】:

    已取消线程的堆栈未展开的断言 - 导致本地对象的不破坏 - 与 @Chris 断言线程的堆栈已展开以及以下反例不一致:

    #include <climits>
    #include <iostream>
    #include <pthread.h>
    #include <thread>
    #include <unistd.h>
    
    class Obj {
    public:
        Obj()  { std::clog << "Obj() called\n"; }
        ~Obj() { std::clog << "~Obj() called\n"; }
    };
    
    static void cleanup(void* arg) {
        std::clog << "cleanup() called\n";
    }
    
    static void run() {
        Obj obj{}; // Local object
        pthread_cleanup_push(cleanup, nullptr);
        ::pause(); // Thread cancelled here
        pthread_cleanup_pop(1);
    }
    
    int main(int argc, char **argv) {
        std::thread thread([]{run();});
        ::sleep(1);
        ::pthread_cancel(thread.native_handle());
        thread.join();
    }
    

    当执行时,上面的程序表明本地对象的析构函数确实在线程被取消时被调用:

    $ ./a.out 
    Obj() called
    cleanup() called
    ~Obj() called
    $ 
    

    【讨论】:

      猜你喜欢
      • 2016-03-26
      • 1970-01-01
      • 2019-07-15
      • 2016-03-26
      • 1970-01-01
      • 2011-09-29
      • 2020-07-23
      • 2017-09-04
      • 1970-01-01
      相关资源
      最近更新 更多