2

C++ のクラスのメンバー関数で pthread を使用する「典型的な」アプローチは、継承を使用することです (ここで提案されているようにhttps://stackoverflow.com/a/1151615/157344 )。しかし、なぜこのようなものではないのですか:

#include <pthread.h>

template <class T, void * (T::*thread)()>
class Thread
{
public:
    int create(T *that) { return pthread_create(&_handle, nullptr, _trampoline, that); };
    pthread_t getHandle() const { return _handle; };

private:
    static void * _trampoline(void *that) { return (static_cast<T *>(that)->*thread)(); };

    pthread_t _handle;
};

次のように使用できます。

class SomeClassWithThread
{
public:
    int initialize() { return _thread.create(this); };

private:
    void * _threadFunction();
    Thread<SomeClassWithThread, &SomeClassWithThread::_threadFunction> _thread;
};

仮想関数を使用しないという利点があるため、vtable がなく、使用される RAM が少なくなります (PC ではなく MCU 用に開発しているため、RAM の使用が重要です)。また、仮想デストラクタも必要ありません。

さらに、典型的なオブジェクトは IS-A スレッド (継承) よりもむしろ HAS-A スレッド (構成) を持っているので、より理にかなっていると思いますよね? (;

継承方法とは対照的に、どこにも提案されていないので、そのような設計に欠陥はありますか? インスタンス化ごとに _trampoline() のコピーを確実に取得しますが、それは継承バージョンの仮想関数呼び出しと大差ありません... create() と getHandle() がインライン化されることを願っています。 ...

4

3 に答える 3

1

これにより、「this」アドレスの問題が解決されます

#include <pthread.h>

template <class T, void * (T::*thread)()>
class Thread
{
public:
    int create(T* passedThis) { return pthread_create(&_handle, nullptr, _trampoline, passedThis); };
    pthread_t getHandle() const { return _handle; };

private:
    static void * _trampoline(void *that) { return (static_cast<T*>(that)->*thread)(); };

    pthread_t _handle;
};

更新された使用法:

class SomeClassWithThread
{
public:
    int initialize() { return _thread.create(this); };

private:
    void * _threadFunction();
    Thread<SomeClassWithThread, &SomeClassWithThread::_threadFunction> _thread;
};
于 2013-01-18T15:08:49.893 に答える
1

Useless が彼の回答で述べたように、厳密に言えば、pthreadライブラリによって呼び出されるスレッド関数は である必要がありますextern "C"。静的メンバー関数は、ほとんどすべてのケースで機能しますが、言語弁護士の観点からすると、少なくとも 1 つの実際の状況からすると、正しくありません。詳細については、 https://stackoverflow.com/a/2068048/12711を参照してください。

ただし、extern "C"関数にライブラリとクラス テンプレート間のインターフェイスを提供させることはできますpthreadが、わずかなオーバーヘッドが必要になるようです。

#include <pthread.h>

struct trampoline_ctx
{
    void* (*trampoline)(void*);
    void* obj;
};

extern "C"
void* trampoline_c(void* ctx)
{
    struct trampoline_ctx* t = static_cast<struct trampoline_ctx*>(ctx);

    return (t->trampoline)(t->obj);
}


template <class T, void * (T::*thread)()>
class Thread
{
public:
    int create(T *that) { 
        ctx.trampoline = _trampoline;
        ctx.obj = that;
        return pthread_create(&_handle, nullptr, trampoline_c, &ctx); 
    };
    pthread_t getHandle() const { return _handle; };

private:
    static void * _trampoline(void *that) { return (static_cast<T *>(that)->*thread)(); };

    pthread_t _handle;
    struct trampoline_ctx ctx;
};

ほとんどの場合、継承よりも構成の方がおそらくスレッド化のより良いモデルであることに同意します。

もちろん、C++11 が提供std::threadするテンプレート化された非継承設計であることを思い出してください。そしてboost::thread、C++11 がオプションではないかどうかを確認してください。

于 2013-01-19T09:44:54.730 に答える
1

Thread::_trampolineメソッドにはextern "C"リンケージがないため、これは実際には機能する可能性がありますが、正しくありません。テンプレート関数に正しいリンケージを与えることができないため、マクロを許可しない限り、これを自動化する簡単な方法はありません。

さらに、典型的なオブジェクトは IS-A スレッド (継承) よりもむしろ HAS-A スレッド (構成) を持っているので、より理にかなっていると思いますよね? (;

いいえ、それはモデルによって異なります。

  • アクティブなオブジェクトは、多くの場合、単一のスレッドに緊密にバインドされています (そのため、インスタンスは同時実行プロセスであると見なすことができます)。
  • タスク キュー (またはスレッド プール) では、多くの場合オブジェクトであるタスクですが、スレッドはいくつかのスケジューリング ロジックに関連付けられています (キュー/プール自体ももちろんオブジェクトである可能性がありますが、それはあなたが何をしているようには見えません)。提案している)

正直なところ、非常に多くの異なるトップレベル スレッド関数を作成していて、vtables が占有するメモリを心配している場合は、そもそも設計が間違っているのではないかと思います。

于 2013-01-18T15:47:56.837 に答える