在线程中指向此*

时间:2014-08-07 06:34:33

标签: c++ multithreading

我有一个类和一个库(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;

2 个答案:

答案 0 :(得分:1)

这里有两个问题:如果main_thread成员函数是静态成员函数,则使用&lwip::main_thread传递指向它的指针,不需要任何转换。如果该功能 static,则必须将其设为static

另一个问题是,如果传递给线程函数的实例(this)被破坏,则线程函数现在具有指向被破坏对象的指针。注意临时对象或按值传递实例。


如果实际的线程函数不能是static,您可以使用静态包装函数轻松解决它:

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);

答案 1 :(得分:0)

如果必须将指针强制转换为要获取的函数 要编译pthread_create,您有未定义的行为。

如果目标是在不同的线程中调用成员函数, 你需要在extern "C"函数中包装调用。这个 意味着没有成员,没有模板;在最简单的情况下:

extern "C" void*
startThread( void* p )
{
    static_cast<T*>(p)->f();
}

并传递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。抓住它是一个非常好的主意 std::unique_ptr中的doStartThread,尽管你仍然想要 捕获此函数中的异常,否则它们会 杀了这个过程。不要忘记delete if pthread_create失败(因为调用者已经过去了 所有权。如果你真的想确定:

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对象本身, 并且它可能指向的任何东西。