4

オブジェクトをpthread_create関数に渡す方法について少し混乱しています。void *へのキャスト、pthread_createへの引数の受け渡しなどに関する断片的な情報をたくさん見つけましたが、それをすべて結び付けるものは何もありません。私はそれをすべて一緒に結び付けて、愚かなことを何もしていないことを確認したいだけです。次のスレッドクラスがあるとしましょう。 編集:不一致を修正しましたstatic_cast

class ProducerThread {
    pthread_t thread;
    pthread_attr_t thread_attr;
    ProducerThread(const ProducerThread& x);
    ProducerThread& operator= (const ProducerThread& x);
    virtual void *thread_routine(void *arg) {
        ProtectedBuffer<int> *buffer = static_cast<ProtectedBuffer<int> *> arg;
        int randomdata;

        while(1) {
            randomdata = RandomDataGen();
            buffer->push_back(randomdata);
        }

        pthread_exit();
    }
public:
    ProtectedBuffer<int> buffer;

    ProducerThread() {
        int err_chk;

        pthread_attr_init(&thread_attr);
        pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);

        err_chk = pthread_create(&thread, &thread_attr, thread_routine, static_cast<void *> arg);
        if (err_chk != 0) {
            throw ThreadException(err_chk);
        }
    }
    ~ProducerThread() {
        pthread_cancel(&thread);
        pthread_attr_destroy(&thread_attr);
    }
}

明確にするために、クラス内のデータには、ミューテックスを使用して実際のデータを保護するProtectedBufferなどのメソッドでのみアクセスできます。ProtectedBuffer::push_back(int arg)

私の主な質問は:私はstatic_cast正しく使用していますか?そして、私の2番目の質問はvirtual void *thread_routine(void *arg)、渡されたvoidポインターをへのポインターにコピーする最初の行が必要ProtectedBufferですか?

また、問題を引き起こす可能性のある他のことをした場合は、それを聞いていただければ幸いです。

4

2 に答える 2

4

コードには多くの問題があります。手始めに、argあなたがキャストしている場所がどこで宣言されているのかわからないので、ケースが適切かどうかはわかりません。

おそらくもっと重要なのthread_routineは、メンバー関数であるため、関数へのポインターに変換することはできません。渡される関数はで pthread_createある必要がextern "C"あるため、メンバー、期間にすることはできません。それは自由な関数declareでなければなりませんextern "C"。メンバー関数を呼び出す場合は、最後の引数としてオブジェクトへのポインターを渡し、関数でそれを逆参照しextern "C"ます。

extern "C" void* startProducerThread( void* arg )
{
    return static_cast<ProducerThread*>( arg )->thread_routine();
}

そして、スレッドを開始するには:

int status = pthread_create( &thread, &thread_attr, startProducerThread, this );

コンストラクターでこれを行わないでください。他のスレッドは、オブジェクトが完全に構築される前に実行を開始し、悲惨な影響を与える可能性があります。

また、キャストインstartProducerThreadがに 渡されたポインタとまったく同じタイプであることを確認してpthread_createください。の基本クラスにキャストする場合は、それが;startProducerThreadに渡す基本クラスへのポインタであることを非常に確認してください 。pthread_create必要に応じて明示的なキャストを使用します( startProducerThreadではなく、のタイプにvoid*)。

最後に、実際の質問とは関係ありません ProtectedBufferが、のようなインターフェイスがありstd::vector、内部データへの参照を返す場合、スレッドセーフにする方法はありません。保護はクラスの外部にある必要があります。

于 2012-08-08T19:17:33.783 に答える
3

このルートに行きたいのなら、私はあなたがこのようなものが欲しいと信じています:

編集: James Kanzeの回答に基づいて、activate構築が完了した後にスレッドを起動する別のメソッドを追加します。

class GenericThread {
protected:
    GenericThread () {
      //...
    }
    virtual ~GenericThread () {}

    int activate () {
        return pthread_create(..., GenericThreadEntry, this);
    }

    virtual void * thread_routine () = 0;

    #if 0
    // This code is wrong, because the C routine callback will do so using the
    // C ABI, but there is no guarantee that the C++ ABI for static class methods
    // is the same as the C ABI.
    static void * thread_entry (void *arg) {
        GenericThread *t = static_cast<GenericThread *>(arg);
        return t->thread_routine();
    }
    #endif
};

extern "C" void * GenericThreadEntry (void *) {
    GenericThread *t = static_cast<GenericThread *>(arg);
    return t->thread_routine();
}

次に、ProducerThreadから派生しGenericThreadます。

編集: C++標準での検索。extern "C"Cライブラリルーチンで呼び出すには、関数ポインタがCリンケージを持つ関数を指している必要があるという要件はありません。ポインタが渡されているため、名前を解決するためにリンケージが使用されるため、リンケージ要件は適用されません。C ++ 2011ドラフト(n3242)、Sec。によると、静的メソッドへのポインターは関数ポインターです。3.9.2p3:

静的メンバーへのポインターを除いて、ポインターを参照するテキストはメンバーへのポインターには適用されません。

編集: Meaculpa。Cライブラリは、Cアプリケーションのバイナリインターフェイスを想定してコールバック関数を呼び出します。C ++リンケージを持つ関数は、CABIとは異なるABIを使用する場合があります。extern "C"これが、Cライブラリへのコールバック関数に渡すときにリンケージ付きの関数を使用する必要がある理由です。ジェームズ・カンゼを疑ってくれたことを心からお詫びします。また、ロキ・アスタリに私を拘束してくれたことに心から感謝します。

于 2012-08-08T19:09:15.587 に答える