26

私は、スレッドローカルストレージが優れているという回答にコメントしていましたが、私が想定していた例外に関する別の有益な議論を思い出しました

throw ブロック内の実行環境に関する唯一の特別な点は、例外オブジェクトが rethrow によって参照されることです。

2 つと 2 つをまとめると、メイン関数の関数キャッチ ブロック内でスレッド全体を実行すると、スレッド ローカル ストレージが吹き込まれませんか?

ゆっくりではありますが、うまく機能しているようです。これは小説ですか、それともよく特徴付けられていますか? 問題を解決する別の方法はありますか?私の最初の前提は正しかったですか?プラットフォームでどのようなオーバーヘッドがget_thread発生しますか? 最適化の可能性は?

#include <iostream>
#include <pthread.h>
using namespace std;

struct thlocal {
    string name;
    thlocal( string const &n ) : name(n) {}
};

struct thread_exception_base {
    thlocal &th;
    thread_exception_base( thlocal &in_th ) : th( in_th ) {}
    thread_exception_base( thread_exception_base const &in ) : th( in.th ) {}
};

thlocal &get_thread() throw() {
    try {
        throw;
    } catch( thread_exception_base &local ) {
        return local.th;
    }
}

void print_thread() {
    cerr << get_thread().name << endl;
}

void *kid( void *local_v ) try {
    thlocal &local = * static_cast< thlocal * >( local_v );
    throw thread_exception_base( local );
} catch( thread_exception_base & ) {
    print_thread();

    return NULL;
}

int main() {
    thlocal local( "main" );
    try {
        throw thread_exception_base( local );
    } catch( thread_exception_base & ) {
        print_thread();

        pthread_t th;
        thlocal kid_local( "kid" );
        pthread_create( &th, NULL, &kid, &kid_local );
        pthread_join( th, NULL );

        print_thread();
    }

    return 0;
}

これには、から派生した新しい例外クラスを定義しthread_exception_base、ベースを で初期化する必要がありget_thread()ますが、全体として、これは非生産的な不眠症に悩まされている日曜日の朝のようには感じられません…</p>

編集: GCC がinを3 回呼び出しているようです。編集:最初のウォークスルーで見逃したブロックを見つけるために、スタック、環境、実行可能形式への多くの厄介な内省。GCC がOS から呼び出しているため、これはプラットフォームに大きく依存しているように見えます。約 4000 サイクルのオーバーヘッド。クラス階層もトラバースする必要があると思いますが、それは制御下に置くことができます。pthread_getspecificget_threadcatchlibunwind

4

4 に答える 4

10

質問の遊び心のある精神で、私はこの恐ろしい悪夢の創造物を提供します:

class tls
{
    void push(void *ptr)
    {
        // allocate a string to store the hex ptr 
        // and the hex of its own address
        char *str = new char[100];
        sprintf(str, " |%x|%x", ptr, str);
        strtok(str, "|");
    }

    template <class Ptr>
    Ptr *next()
    {
        // retrieve the next pointer token
        return reinterpret_cast<Ptr *>(strtoul(strtok(0, "|"), 0, 16));
    }

    void *pop()
    {
        // retrieve (and forget) a previously stored pointer
        void *ptr = next<void>();
        delete[] next<char>();
        return ptr;
    }

    // private constructor/destructor
    tls() { push(0); }
    ~tls() { pop(); }

public:
    static tls &singleton()
    {
        static tls i;
        return i;
    }

    void *set(void *ptr)
    {
        void *old = pop();
        push(ptr);
        return old;
    }

    void *get()
    {
        // forget and restore on each access
        void *ptr = pop();
        push(ptr);
        return ptr;
    }
};

C++ 標準strtokに従って、最初の引数を隠して、後続の呼び出しが0同じ文字列からさらにトークンを取得できるようにするという事実を利用するため、スレッド対応の実装では TLS を使用する必要があります。

example *e = new example;

tls::singleton().set(e);

example *e2 = reinterpret_cast<example *>(tls::singleton().get());

したがって、プログラムの他の場所で意図した方法で使用されない限り、strtok別の予備の TLS スロットがあります。

于 2010-07-02T15:09:37.260 に答える
3

私はあなたがここにいると思います。これは、スレッドの明示的な使用とは別に、前述のように、ユーザーの「状態」変数を受け入れないコールバックにデータを取得する移植可能な方法でさえあるかもしれません。

件名の質問に「はい」と答えたようですね。

于 2010-03-21T18:01:33.140 に答える
0
void *kid( void *local_v ) try {
    thlocal &local = * static_cast< thlocal * >( local_v );
    throw local;
} catch( thlocal & ) {
    print_thread();

    return NULL;
}

==

void *kid (void *local_v ) { print_thread(local_v); }

ここで何かが欠けているかもしれませんが、それはスレッドローカルストレージではなく、不必要に複雑な引数の受け渡しです。引数がスレッドごとに異なるのは、引数が pthread_create に渡されるためであり、例外ジャグリングのためではありません。


この例では、GCC が実際のスレッド ローカル ストレージ呼び出しを生成していることを実際に見逃していたことが判明しました。それは実際に問題を興味深いものにします。それが他のコンパイラの場合であるかどうか、またスレッドストレージを直接呼び出すこととどう違うのか、私にはまだよくわかりません。

私は、引数、スタック ウォーキング、スレッド ローカル ストレージなど、同じデータにもっと単純で簡単な方法でアクセスできるという私の一般的な主張を今でも支持しています。

于 2010-03-21T15:52:04.167 に答える
0

現在の関数呼び出しスタックのデータへのアクセスは、常にスレッド セーフです。これが、コードがスレッド セーフである理由であり、例外を巧みに使用したためではありません。スレッド ローカル ストレージを使用すると、スレッドごとのデータを格納し、直接の呼び出しスタックの外部で参照できます。

于 2010-03-21T17:37:40.010 に答える