2

我正在用 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);
}
4

3 回答 3

2

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

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

于 2010-11-19T12:25:05.317 回答
2

已取消线程的堆栈未展开的断言 - 导致本地对象的不破坏 - 与@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
$ 
于 2017-05-01T22:19:18.653 回答
1

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

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

克里斯

于 2011-01-31T10:36:52.810 回答