4

I'm a little confused about how to pass an object to the pthread_create function. I've found a lot of piecemeal information concerning casting to void*, passing arguments to pthread_create, etc., but nothing that ties it all together. I just want to make sure I've tied it all together and am not doing anything stupid. Let's say I have the following thread class: Edit: fixed mis-matched 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);
    }
}

To clarify, the data in the ProtectedBuffer class can only be accessed with methods like ProtectedBuffer::push_back(int arg), which use mutexes to protect the actual data.

My main question is: am I using static_cast correctly? And my secondary question is do I need that first line in virtual void *thread_routine(void *arg) where I copy the passed void pointer to a pointer to ProtectedBuffer?

Also, if I've done anything else that might cause problems, I'd appreciate hearing it.

4

2 回答 2

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;必要时使用显式强制转换(对 in 的类型 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:

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

编辑: 过失。C 库将调用假定 C 应用程序二进制接口的回调函数。具有 C++ 链接的函数可能使用与 C ABI 不同的 ABI。extern "C"这就是为什么在将回调函数传递给 C 库时需要使用具有链接的函数的原因。我对 James Kanze 对他的怀疑表示诚挚的歉意,并衷心感谢 Loki Astari 让我直言不讳。

于 2012-08-08T19:09:15.587 回答