首页 文章

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

提问于
浏览
4

我发现了很多关于转换为void *,将参数传递给pthread_create等的零碎信息,但没有任何内容将它们联系在一起 . 我只是想确保我've tied it all together and am not doing anything stupid. Let' s说我有以下线程类: Edit: 已修复错误匹配 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 吗?我的第二个问题是我需要_490108中的第一行,我将传递的void指针复制到指向 ProtectedBuffer 的指针?

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

2 回答

  • 3

    如果你想走这条路,我相信你想要这样的东西:

    Edit: 根据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 .

    Edit: 在C标准中搜索extern“C” . 没有要求函数指针必须指向C链接可以被C库例程调用的函数 . 由于指针正在传递,因此链接要求不适用,因为链接用于解析名称 . 根据C 2011草案(n3242),Sec . 指向静态方法的指针是一个函数指针 . 3.9.2p3:

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

    Edit: Mea culpa . 假设C应用程序二进制接口,C库将调用回调函数 . 具有C链接的函数可以使用与C ABI不同的ABI . 这就是为什么在将回调函数传递给C库时需要使用具有 extern "C" 链接的函数 . 我真诚地向James Kanze表示怀疑,并对Loki Astari让我感到非常不满 .

  • 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 的基类的指针;必要时使用显式强制转换(对于 startProducerThread 中的类型,而不是 void* ) .

    最后,虽然与您的实际问题无关:如果 ProtectedBuffer 具有类似 std::vector 的接口,并返回对内部数据的引用,则无法使其线程安全 . 保护需要在课堂外部 .

相关问题