我对如何将对象传递给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
的指针?
另外,如果我做了其他可能导致问题的事情,我会很感激听到它。
答案 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
;必要时使用显式强制转换(对于中的类型)
startProducerThread
,不到void*
)。
最后,虽然与您的实际问题无关:如果
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让我感到不快。