转换为void *以在c ++中将对象传递给pthread

时间:2012-08-08 18:58:18

标签: c++ pointers pthreads static-cast

我对如何将对象传递给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吗?我的第二个问题是我是否需要virtual void *thread_routine(void *arg)中的第一行,我将传递的void指针复制到指向ProtectedBuffer的指针?

另外,如果我做了其他可能导致问题的事情,我会很感激听到它。

2 个答案:

答案 0 :(得分:4)

您的代码存在许多问题。对于初学者,我没有 看看你所投射的arg在哪里,所以我不知道是否 这个案子是合适的。

也许更重要的是,thread_routine是一个成员函数,所以它 无法转换为指向函数的指针。函数传递给 pthread_create必须为extern "C",因此不能成为会员,期间; 它必须是一个自由函数声明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;必要时使用显式强制转换(对于中的类型) startProducerThreadvoid*)。

最后,虽然与您的实际问题无关:如果 ProtectedBuffer的界面类似std::vector的界面 返回对内部数据的引用,你无法做到 线程安全。保护需要在课堂外部。

答案 1 :(得分: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 draft(n3242),Sec。指向静态方法的指针是一个函数指针。 3.9.2p3:

  

除了指向静态成员的指针外,引用指针的文本不适用于指向成员的指针。

编辑: Mea culpa。 C库将在假设C应用程序二进制接口的情况下调用回调函数。具有C ++链接的函数可以使用与C ABI不同的ABI。这就是为什么在向C库传递回调函数时需要使用具有extern "C"链接的函数。我真诚地向詹姆斯坎泽道歉,因为他怀疑他,并衷心感谢Loki Astari让我感到不快。