2

C++ で Linux 用の MT プログラムを書いていますが、スレッドのキャンセルがどのように行われるか知りたいです。

私が理解している限り、スレッドがキャンセルされると、クリーンアップ関数がスレッドの関数内で呼び出され、スレッドの関数が強制的に終了されます。これは次の 2 つのことを意味します。

  1. スレッドがキャンセルされても、スレッドの関数内で作成されたすべての C++ オブジェクトのデストラクタを呼び出します。
  2. スレッドの関数で作成されたオブジェクトへのポインターをクリーンアップ関数に渡すことができます。

私は正しく、以下のコードは問題なく動作しますか?


以下のコードでもう 1 つ質問があります。スレッドが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 に答える