在静态方法中转换为自指针会在(派生)方法调用上抛出段错误

时间:2015-02-23 10:56:25

标签: c++ multithreading

我正在尝试实现一个简单的线程入门类。下面是一个简单基类实现和两个应该作为启动器的派生变体。第一个偶尔抛出static void* Threaded::run (void* self)的段错误。我想这可能是一个指针问题,但我无法找出原因?

this中的Threaded::start是否指向错误的地址,或者我的第一次推导是否存在任何其他问题?

这就是它的用法:

Thread thread (ptr_to_some_obj);
thread.start (&this_obj::callback);
thread.detach ();

简单基类

class Threaded
{

public:

    Threaded () {/* empty */}
    virtual ~Threaded () {/* empty */}

    /** Returns true if the thread was successfully started, false if there was an error starting the thread */
    bool start ()
    {
        return (pthread_create (&_thread, NULL, run, this) == 0);
    }

    /** Implement this method in your subclass with the code which allows to gently stop execution. */
    virtual void stop () = 0;

    /** Will not return until the internal thread has exited. */
    void wait ()
    {
        (void) pthread_join (_thread, NULL);
    }

    bool detach ()
    {
        return (pthread_detach (_thread) == 0);
    }

protected:

   /** Implement this method in your subclass with the code you want your thread to run. */
   virtual void run () = 0;

   static void* run (void* self)
   {
       ((Threaded*) self) -> run ();
       return NULL;
   }

   pthread_t _thread;
};

派生类1 (在上面((Threaded*) self) -> run ();处抛出段错误)

typedef void (*staticcall)(void*);

class Thread : public Threaded
{
public:

    Thread (void* passthru)
        :_call (NULL)
    {
        _passthru = passthru;
    }

    ~Thread () { /* empty */ }

    bool start (staticcall call)
    {
        _call = call;
        assert (_call);
        return start ();
    }

    void stop ()
    {
        // nothing
    }

protected:

    void run ()
    {
        (_call) (_passthru);
    }

    bool start ()
    {
        return Threaded::start ();
    }

private:

    Thread () { };
    void* _passthru;
    staticcall _call;
};

派生类2 (有效,但我宁愿派生类1 实施)

typedef void (*staticcall)(void*);

class Thread2 : public Threaded
{
public:

    Thread2 (void* passthru)
    {
        _passthru = passthru;
    }

    ~Thread2 () { /* empty */ }

    bool start (staticcall call)
    {
        _call = call;
        assert (_call);
        return start ();
    }

    void stop ()
    {
        // nothing
    }

protected:

    void run () { }

    static void* run2 (void*)
    {
        (_call) (_passthru);
        return NULL;
    }

    bool start ()
    {
        return (pthread_create (&_thread, NULL, run2, NULL) == 0);
    }

private:

    Thread2 () { };
    static void* _passthru;
    static staticcall _call;
};

void* Thread2::_passthru;
staticcall Thread2::_call;

2 个答案:

答案 0 :(得分:0)

  

编辑:让线程作为方法调用执行的解决方案

我在最近的讨论中考虑的解决方案是如果线程入口点是一个简单的函数。
但是,我认为这个想法更多的是利用实际的对象,因此线程体实际上是body()方法的调用。

这更棘手,因为在整个线程持续时间内必须有派生类的实时实例,但在start / detach之后原始实例很可能超出范围序列

一种可能的(虽然有些代价高昂)解决方案是让线程主体存根在堆栈上创建原始实例的本地副本。所以线程启动器将构造一个线程对象,线程本身将复制它。

使用此系统,您只需确保原始实例将在pthread_create和调度程序激活线程之间的时间间隔内保持实时。

这需要一个信号量(由于血腥的C ++ 11在商店中没有一个信号量,因此将使用互斥锁/ cond.var.对手动完成。)

要在基类中隐藏这个混乱的代码,您需要将基本指针向下转换为适当的子类类型。
我试图模仿基类,尽管可能有更聪明的解决方案。我想不出任何一个。

为了测试解决方案,我使用一个计数器系统来检测在线程存根可以制作本地副本之前是否删除了原始的Thread实例。

SYNC编译标志激活信号量。预期的程序输出为0->0。如果出现其他数字,则表示某些线程在混乱的实例上运行。

我在虚拟机上的Ubuntu上进行了测试,看起来效果还不错。

#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <thread> // sleep_for
#include <chrono> // milliseconds
#include <pthread.h>

#define SYNC // undefine this to see what happens without synchronization

typedef void *(*tEntryPoint) (void *);


#include <mutex>
#include <condition_variable>

class semaphore {
private:
    std::mutex m;
    std::condition_variable v;
    int c;

public:
    semaphore (int count = 0):c(count){}

    void V()
    {
#ifdef SYNC
        std::unique_lock<std::mutex> l(m);
        c++;
        v.notify_one();
#endif
    }
    void P()
    {
#ifdef SYNC
        std::unique_lock<std::mutex> l(m);
        while (c == 0) v.wait(l);
        c--;
#endif
    }
};

template<typename Derived>
class Threaded
{
public:

    /** Returns true if the thread was successfully started, false if there was an error starting the thread */
    bool start(void)
    {
        destructor_guard = new semaphore();
        bool res = (pthread_create(&_thread, NULL, (tEntryPoint)entry_point, this) == 0);
        if (res) destructor_guard->P();      // wait fot thread to start execution
        delete destructor_guard;
        return res;
    }

    /** This optional method will be executed after the thread main body */
    virtual void stop() {}

    /** Will not return until the internal thread has exited. */
    void wait()
    {
        (void)pthread_join(_thread, NULL);
    }

    /** Will let the underlying task run independently */
    bool detach()
    {
        return (pthread_detach(_thread) == 0);
    }

private:
    static void * entry_point(Derived * self)
    {
        Derived local_self = *self;
        local_self.destructor_guard->V(); // original can be deleted
        local_self.body();
        local_self.stop();
        return NULL;
    }

    pthread_t _thread;
    semaphore* destructor_guard;
};

#define NUM_THREADS 9
#define REPEAT 3000
static int signature[NUM_THREADS + 1] = { 0, };

class Thread : public Threaded<Thread>
{
    unsigned id;
public:
    Thread(unsigned id) : id(id) {}
    ~Thread() { id = 0; }

    void body(void)
    {
        signature[id%(NUM_THREADS+1)]++;
    }

    void stop(void)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        signature[id%(NUM_THREADS+1)]++;
    }
};

void launch_a_thread(int id)
{
    Thread thread (id);
    if (thread.start())
    {
//        thread.wait();
        thread.detach();
    }
}

int main(void)
{
    for (unsigned i = 0; i != REPEAT*NUM_THREADS; i++) launch_a_thread(1+i%NUM_THREADS);
    std::this_thread::sleep_for(std::chrono::milliseconds(100)); // leave enough time for free running threads to terminate
    for (int i = 0 ; i <= NUM_THREADS ; i++) if (signature[i] != 2*REPEAT) printf ("%d -> %d\n", i, signature[i]);
    return 0;
}

答案 1 :(得分:0)

正如molbdnilo所指出的那样:

  

pthread_create只排队新线程。无法保证何时调用线程函数,并且thread当时必须处于活动状态。

由于我不想保留生成的线程列表,因此我使用pthread_cond_waitpthread_cond_signal解决了这个问题。生成器将等待由线程中运行的方法发出的信号。这样线程创建者就可以在调用to-be-threaded方法之前销毁线程对象。

class ThreadSpawner
{
public:

    ThreadSpawner ()
    {
        pthread_mutex_init (&MtxThreadStarter, 0);
        pthread_cond_init (&CondThreadStarter, 0);
    }

    ~ThreadSpawner ()
    {
        pthread_cond_destroy (&CondThreadStarter);
        pthread_mutex_destroy (&MtxThreadStarter);
    }

    void spawn ()
    {
        Thread thread (pass_object);
        pthread_mutex_lock (&MtxThreadStarter);
        if (thread.start (&ThreadSpawner::callback))
        {
            // wait here for signal
            pthread_cond_wait (&CondThreadStarter, &MtxThreadStarter);
            thread.detach ();
        }
        pthread_mutex_unlock (&MtxThreadStarter);
    }

    static void callback (void* passthru)
    {
        // send signal to thread spawner
        pthread_mutex_lock (&MtxThreadStarter);
        pthread_cond_signal (&CondThreadStarter);
        pthread_mutex_unlock (&MtxThreadStarter);

        // do threaded work
    }

private:

    static pthread_mutex_t MtxThreadStarter;
    static pthread_cond_t CondThreadStarter;    
}

pthread_mutex_t ThreadSpawner::MtxThreadStarter = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ThreadSpawner::CondThreadStarter = PTHREAD_COND_INITIALIZER;