8

C++11 では、thread_local ストレージを持つ自明でないオブジェクトを持つことができます。

class X { ... }

void f()
{
    thread_local X x = ...;
    ...
}

残念ながら、この機能はまだ gcc に実装されていません (4.7 の時点)。

gcc では、スレッド ローカル変数を使用できますが、自明な型のみを使用できます。

回避策を探しています:

これが私がこれまでに持っているものです:

#include <iostream>
#include <type_traits>

using namespace std;

class X
{
public:
    X() { cout << "X::X()" << endl; };
    ~X() { cout << "X::~X()" << endl; }
};

typedef aligned_storage<sizeof(X), alignment_of<X>::value>::type XStorage;

inline void placement_delete_x(X* p) { p->~X(); }

void f()
{
        static __thread bool x_allocated = false;
        static __thread XStorage x_storage;

        if (!x_allocated)
        {
                new (&x_storage) X;
                x_allocated = true;

                // TODO: add thread cleanup that
                //     calls placement_delete_x(&x_storage)
        }

        X& x = *((X*) &x_storage);
}

int main()
{
        f();
}

私が助けを必要としているのは、現在のスレッドの終了時に placement_delete_x(&x_storage) を呼び出すことです。これを行うために使用できる pthreads や Linux のメカニズムはありますか? 関数ポインターとパラメーターをある種の pthread クリーンアップ スタックに追加する必要がありますか?

アップデート:

pthread_cleanup_pushが欲しいものかもしれないと思います:

http://www.kernel.org/doc/man-pages/online/pages/man3/pthread_cleanup_push.3.html

これは、この使用法に適した状況でクリーンアップ ハンドラーを呼び出しますか?

更新 2:

boost::thread_specific_ptr最終的にはパラメーターを使用pthread_key_createして呼び出すように見えますが、tls クリーンアップ関数を呼び出すには:destructorpthread_cleanup_push

http://pubs.opengroup.org/onlinepubs/009696799/functions/pthread_key_create.html

これら 2 つの方法の違いがあるとしても、それが何であるかは不明です。?

4

2 に答える 2

3

pthread_key_create and friends are what you'd want to implement thread-specific variables of types with destructors. However, these generally require you to manage the whole process of creating and destroying the variables, and I'm not sure whether you could use them in conjunction with __thread.

pthread_cleanup_push is not suitable. It's intended to allow a resource to be released if the thread exits during a (short) block of code that uses that resource; as described in the documentation you link to, it must be matched by a pthread_cleanup_pop at the same level of that function, and the handler won't be called if the thread returns from its main function. That means that you can't use it if you want the thread-local variable to persist between calls to the function.

For the benefit of those who don't have a prohibition against third-party libraries, Boost provides a convenient, portable way to manage thread-local storage.

于 2012-08-21T09:55:54.567 に答える
3

マイクが言うようpthread_cleanup_pushに、適切ではありません。を使用するのが正しい方法ですpthread_key_create

その方法を示すために、小さなデモ プログラムを実装しました。次のように使用するマクロを実装thread_localします。

実際の C++11 機能では、次のようになります。

void f()
{
    thread_local X x(1,2,3);
    ...
}

これにより、次のようになります。

void f()
{
    thread_local (X, x, 1, 2, 3);
    ...
}

これと boost::thread_specifc_ptr の違いは、動的メモリ割り当てがゼロであることです。すべてが__thread期間とともに保存されます。また、大幅に軽量化されていますが、gcc/linux 固有です。

概要:

  1. 以前std::aligned_storageは、変数の __thread 期間スペースを作成していました
  2. 特定のスレッドからの最初のエントリで、placement new を使用してストレージ内の変数を構築します
  3. また__thread、配置削除呼び出しにリンクされたリスト エントリを割り当てます。
  4. pthread_setspecific各スレッドリストヘッドを追跡するために使用します
  5. に渡された関数はpthread_key_create、スレッドの終了時にプレースメントの削除を呼び出すリストをウォークします。

...

#include <iostream>
#include <thread>

using namespace std;

static pthread_key_t key;
static pthread_once_t once_control = PTHREAD_ONCE_INIT;

struct destructor_list
{
    void (*destructor)(void*);
    void* param;
    destructor_list* next;
};

static void execute_destructor_list(void* v)
{
    for (destructor_list* p = (destructor_list*) v; p != 0; p = p->next)
        p->destructor(p->param);
}

static void create_key()
{
    pthread_key_create(&key, execute_destructor_list);
}

void add_destructor(destructor_list* p)
{
    pthread_once(&once_control, create_key);

    p->next = (destructor_list*) pthread_getspecific(key);
    pthread_setspecific(key, p);
}

template<class T> static void placement_delete(void* t) { ((T*)t)->~T(); }

#define thread_local(T, t, ...)                         \
T& t = *((T*)                                           \
({                                                      \
    typedef typename aligned_storage<sizeof(T),         \
        alignment_of<T>::value>::type Storage;          \
    static __thread bool allocated = false;             \
    static __thread Storage storage;                    \
    static __thread destructor_list dlist;              \
                                                        \
    if (!allocated)                                     \
    {                                                   \
        new (&storage) T(__VA_ARGS__);                  \
        allocated = true;                               \
        dlist.destructor = placement_delete<T>;         \
        dlist.param = &storage;                         \
        add_destructor(&dlist);                         \
    }                                                   \
                                                        \
    &storage;                                           \
}));

class X
{
public:
    int i;

    X(int i_in) { i = i_in; cout << "X::X()" << endl; };

    void f() { cout << "X::f()" << endl; }

    ~X() { cout << "X::~X() i = " << i << endl; }
};

void g()
{
    thread_local(X, x, 1234);
    x.f();
}

int main()
{
    thread t(g);
    t.join();
}

ノート:

  1. 各 pthread_* 呼び出しにエラー チェックを追加する必要があります。展示の為取り外しました。
  2. __threadGNU拡張であるを使用します
  3. 式ステートメントを使用して、補助的な __thread 変数名を親スコープから除外します。これも GNU 拡張です。
于 2012-08-21T11:30:15.840 に答える