【问题标题】:QNX c++ thread questionQNX c++线程问题
【发布时间】:2009-01-11 17:05:13
【问题描述】:

我有一个关于我想在 QNX 上运行的代码的问题:

class ConcreteThread : public Thread
{
public:
    ConcreteThread(int test)
    {
        testNumber = test;
    }

    void *start_routine() 
    { 
        for(int i = 0; i < 10; i++)
        {
            sleep(1);
            cout << testNumber << endl;
        }   
    }

private:
    int testNumber;
};




class Thread 
{
public:
    Thread(){};

    int Create()
    {
        pthread_t m_id;
        return pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this);
    }

protected:
    virtual void *start_routine() = 0;

private:

    static void *start_routine_trampoline(void *p)
    {
        Thread *pThis = (Thread *)p;
        return pThis->start_routine();
    }
};

现在,当我在 *start_routine 中没有休眠的情况下运行此代码时,它会简单地打印数字 10 次,然后继续执行下一行代码(顺序而不是并行)。但是,当我在代码中使用 sleep 时,它根本不会打印任何数字,而是继续执行下一行代码。为什么 sleep 不起作用,我怎样才能使这样的线程工作,而不是按顺序运行?

【问题讨论】:

    标签: c++ multithreading qnx


    【解决方案1】:

    注意 1:如果您只有 1 个处理器,则无论您创建多少线程,代码都只能按顺序完成。在为下一个线程换出之前,每个线程都会获得一段处理器时间。

    注意 2:如果主线程退出,pthreads 将在所有子线程有机会执行之前将其杀死。

    现在回答你的问题:

    没有睡眠。线程一旦启动,就有足够的时间在给定的单个切片中完整地执行循环 10 次。

    休眠:您的工作线程将休眠一整秒。所以你的主线程有时间做很多工作。如果此时主线程退出,worker将被杀死。

    我会做出以下改变:

    //  Remove the Create() method
    //  Put thread creation in the constructor.
    //  Make the thread variable part of the object
    
    pthread_t m_id;
    
    Thread()
    {
        if (pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this) != 0)
        {
            throw std::runtime_error("Thread was not created");
        }
    }
    
    // Make sure the destructor waits for the thread to exit.
    ~Thread()
    {
        pthread_join(m_id);
    }
    

    如果你去看看 boost threading 库。你会发现像这样的小错误都已经被处理好了;从而使线程更易于使用。

    还要注意。使用静态可能有效,但它是不可移植的。这是因为 pthread 是一个 C 库,因此需要一个带有 C ABI 的函数指针。您在这里为您的平台感到幸运。您需要将其定义为函数并使用 extern "C" 声明 ABI

    // This needs to be a standard function with C Interface.
    extern "C" void *start_routine_trampoline(void *p)
    {
    }
    

    【讨论】:

    【解决方案2】:

    尝试使 pthread_t id 成为类成员而不是函数局部变量。这样调用者可以 pthread_join 它。

    不这样做在技术上是资源泄漏(除非线程特别不可连接)。而且加入会避免Martin York描述的问题。

    来自 man pthread_join:

    加入的线程必须处于可加入状态:它不能有 已使用 pthread_detach(3) 或 PTHREAD_CREATE_DETACHED 分离 pthread_create(3) 的属性。

    当一个可连接线程终止时,它的内存资源(线程 描述符和堆栈)在另一个线程执行之前不会被释放 pthread_join 就可以了。因此,pthread_join 必须调用一次 为避免内存泄漏而创建的每个可连接线程。

    【讨论】:

    • 我通常发现通过信号量分离和同步更容易。
    【解决方案3】:

    在这里切线...关于Martin York's post:

    还要注意。使用静态可能有效,但它是不可移植的。这是因为 pthread 是一个 C 库,因此需要一个带有 C ABI 的函数指针。您在这里为您的平台感到幸运。您需要将其定义为函数并使用 extern "C"

    声明 ABI // 这需要是具有 C 接口的标准函数.
    extern "C" void * start_routine_trampoline(void * p) {...}

    我不太确定...

    (1) C++ 被设计为尽可能与 C 兼容。 There are a few differences... 但我的印象是 extern "C" 主要用于规避实现 C++ 函数重载所需的名称修改。

    (2) 看起来,一旦你有了函数指针,调用约定(什么被压入堆栈以进行函数调用)在 C 和 C++ 之间必须相同。否则,函数指针如何工作?

    例如:

    C 代码:

    void bar( int i ) { printf( "bar %d\n", i ); }
    

    C++ 代码:

    class Foo
    {
    public:
      static void foo( int i ) { cout << "foo " << i << endl; }
    };
    
    extern "C" { void bar(int); }
    
    int main()
    {
      void (*p)(int);
    
      p = & Foo::foo;
      (*p)(1);
    
      p = & bar;
      (*p)(2);
    }
    

    【讨论】:

    • 静态类函数没有关于调用约定的保证,它们通常以与 C 约定兼容的方式实现:请参阅parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2 并阅读底部的注释。
    • 令人着迷。因此,编译器在函数指针上附加额外的存储空间以指示它们是否指向 C 与 C++ 函数是合法的。
    • 愚蠢,增加 sizeof(p),削弱 C/C++ 兼容性,给编码增加不必要的痛苦,等等。但这是合法的。谢谢!并感谢您的参考!很高兴知道!
    • extern "C" 确保函数使用 "C" ABI。这在任何地方都有明确的定义和标准。故意未定义 C++ ABI。允许实现尽可能高效地定义 ABI,(这意味着参数可以在寄存器而不是堆栈中传递)
    • 这也是为什么不能链接由不同 C++ 编译器生成的对象的原因(因为它们可能使用不同的 ABI)。但是无论你使用什么 C 编译器,ABI 总是相同的,因此是兼容的。
    猜你喜欢
    • 1970-01-01
    • 2022-01-08
    • 2011-09-30
    • 1970-01-01
    • 2011-07-28
    • 1970-01-01
    相关资源
    最近更新 更多