pthread作为c++中类的成员函数——为什么不使用模板呢?



在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;
};

它的优点是不使用虚拟函数,所以没有虚表和更少的RAM使用(我正在为MCU开发,而不是PC,所以RAM使用很重要)。它也不需要虚析构函数。

而且我认为这更有意义,因为一个典型的对象更倾向于ha - a线程(组合),而不是IS-A线程(继承),对吧?(;

与继承方法相反,这种设计是否有任何缺陷,因为我没有在任何地方看到它的建议?对于每个实例化,你肯定会得到_trampoline()的副本,但这与继承版本中的虚函数调用没有太大不同……我希望create()和getHandle()是内联的,因为没有理由不…

这解决了" 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;
};

您的Thread::_trampoline方法没有extern "C"链接,因此,尽管这可能在实践中起作用,但这是不正确的。因为你不能给模板函数提供正确的链接,所以没有一种简单的方法来自动化这个,除非你允许使用宏。

而且我认为这更有意义,因为一个典型的对象更倾向于ha - a线程(组合),而不是IS-A线程(继承),对吧?(;

不,这取决于你的模型。

  • 活动对象经常紧密地绑定到单个线程(所以你可以考虑一个实例IS-A并发执行进程)
  • 与任务队列(或线程池),它经常是任务是对象,而线程是与一些调度逻辑(队列/池本身可能是一个对象,当然,但这似乎不是你的建议)

老实说,如果你创建了这么多不同的顶级线程函数,你担心变量表占用的内存,我怀疑你的设计首先是错误的。

正如Useless在回答中提到的,严格来说,pthread库调用的线程函数需要是extern "C"。虽然静态成员函数在几乎所有情况下都有效,但从语言律师的角度和至少一种实际情况来看,它是不正确的。详见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,这是一种模板化的非继承设计。如果c++ 11没有选项,请参阅boost::thread

最新更新