[英]Pointer to this* in thread
我有一堂课和一个图书馆(lwip)。 由于某些原因,我需要调用库的线程创建函数,例如:
/** The only thread function:
* Creates a new thread
* @param name human-readable name for the thread (used for debugging purposes)
* @param thread thread-function
* @param arg parameter passed to 'thread'
* @param stacksize stack size in bytes for the new thread (may be ignored by ports)
* @param prio priority of the new thread (may be ignored by ports) */
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int
stacksize, int prio);
在此函数内部,我们称为pthread:
code = pthread_create(&tmp,NULL,(void *(*)(void *)) function, arg);
我的电话看起来像:
sys_thread_new("main_thread",(lwip_thread_fn)&this->main_thread, NULL,
DEFAULT_THREAD_STACKSIZE,DEFAULT_THREAD_PRIO);
我的类方法工作正常,但是我需要更改一些CURRENT类(例如'state'或其他),我有一个主意,将指向当前类的指针传递给该线程,并在线程函数中更改类字段。 一些:
sys_thread_new("main_thread",(lwip_thread_fn)&this->main_thread, (void*)this,
DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
然后在main_thread中:
void lwip::main_thread(void *arg) {
lwip *p = (lwip*)arg;
p->state = 1;
}
这样的事情。 但似乎我做错了-
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff6e8e700 (LWP 4985)]
0x0000000000403a75 in lwip::main_thread (this=0x7fffffffe4f0, arg=0x80) at
../src/lwip.cpp:50
50 p->state = 1;
这里有两个问题:如果main_thread
成员函数是静态成员函数,则使用&lwip::main_thread
传递指向它的指针,则不需要强制转换。 如果函数不是 static
,则必须使其static
。
另一个问题是,如果传递给线程函数的实例( this
)被破坏,则线程函数现在具有指向被破坏对象的指针。 注意临时对象或按值传递实例。
如果实际的线程函数不能是static
,则可以使用static wrapper函数轻松解决:
class lwip
{
...
private:
void main_thread() { ... }
static void* main_thread_wrapper(void* arg)
{
reinterpret_cast<lwip*>(arg)->main_thread();
return nullptr;
}
};
...
sys_thread_new("main_thread", &lwip::main_thread_wrapper, this,
DEFAULT_THREAD_STACKSIZE,DEFAULT_THREAD_PRIO);
如果必须将指针转换为函数以编译pthread_create
,则您将具有未定义的行为。
如果目标是在另一个线程中调用成员函数,则需要将调用包装在extern "C"
函数中。 这意味着没有成员,也没有模板; 在最简单的情况下:
extern "C" void*
startThread( void* p )
{
static_cast<T*>(p)->f();
}
并将startThread
的地址作为第三个参数传递,并将指向该对象的指针作为第四个传递。 如果涉及继承,则必须确保第四个参数的类型与startThread
中的startThread
类型相同,例如:
pthread_create( &tmp, nullptr, &startThread, static_cast<Base*>( pointerToDerived ) );
如果startThread
转换为Base*
。
如果还需要函数的参数,则需要将指向该对象的指针和其他参数传递给结构的指针。 您还需要确保该结构的生存期足够长,这样就不会有线程访问已经不存在的对象的风险。 这通常意味着一个附加的条件变量,以确保在新线程复制所有相关数据之前,调用pthread_create
的线程不会继续。 (Boost线程和C ++ 11线程都为您执行此操作。只有在新线程中需要除指向对象的指针之外的其他数据时才需要这样做。)
如果您需要为许多不同的类型执行此操作,则可能会很痛苦,而如果所涉及的类是模板,则这是完全不可能的。 在这种情况下,一种常见的解决方案是按照以下方式使用Thread
对象:
class Thread
{
public:
virtual void* run() = 0;
};
和启动功能:
namespace {
extern "C" void*
doStartThread( void* p )
{
return static_cast<Thread*>( p )->run();
}
}
pthread_t
startThread( Thread* thread )
{
pthread_t results;
if ( pthread_create( &results, nullptr, doStartThread, thread ) != 0 ) {
throw std::runtime_error( "Could not create thread" );
}
}
然后,您从Thread
继承,用所需的任何内容覆盖run
函数(并添加您可能需要的任何其他数据); 派生类甚至可以是模板。
同样, Thread
对象的生存期是一个问题。 我通常使用的解决方案是要求对其进行动态分配,然后在doStartThread
的末尾将其删除。 在doStartThread
的std::unique_ptr
中捕获它是一个好主意,尽管您仍然想在此函数中捕获异常,因为否则它们将杀死进程。 而且,如果pthread_create
失败,也不要忘记delete
(因为调用者已将所有权移交给他人。如果您确实要确保:
namespace {
extern "C" void*
doStartThread( void* p )
{
std::unique_ptr<Thread*> object( static_cast<Thread*>( p ) );
try {
return object->run();
} catch ( ... ) {
return somethingElseToReportTheError;
}
}
}
pthread_t
startThread( std::unique_ptr<Thread> thread )
{
pthread_t results;
if ( pthread_create( &results, nullptr, doStartThread, thread.get() ) != 0 ) {
throw std::runtime_error( "Could not create thread" );
}
thread.release(); // AFTER the call has succeeded!
}
我已经在许多应用程序中成功使用了此技术(使用std::auto_ptr
,因为那时没有std::unique_ptr
); 通常,您不需要使用动态分配这一事实就不成问题了,它可以很好地解决生命周期问题。 (另一种选择是使用条件变量,阻塞原始线程,直到新线程复制完所有内容为止。)
请注意,通过在接口中使用unique_ptr
,可以有效地阻止调用线程进一步访问线程对象,方法是抢夺它指向对象的指针。 这为线程安全性提供了额外的保证。 但是,当然,这种额外的保证(以及生存期问题的解决方案)仅适用于Thread
对象本身, 而不适用于它可能指向的任何对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.